aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yaml1
-rw-r--r--Cargo.lock73
-rw-r--r--crates/ra_arena/src/map.rs10
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs100
-rw-r--r--crates/ra_cargo_watch/src/lib.rs405
-rw-r--r--crates/ra_flycheck/Cargo.toml (renamed from crates/ra_cargo_watch/Cargo.toml)3
-rw-r--r--crates/ra_flycheck/src/conv.rs (renamed from crates/ra_cargo_watch/src/conv.rs)0
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/test.rs (renamed from crates/ra_cargo_watch/src/conv/test.rs)0
-rw-r--r--crates/ra_flycheck/src/lib.rs340
-rw-r--r--crates/ra_hir_def/src/body/lower.rs54
-rw-r--r--crates/ra_hir_expand/src/proc_macro.rs27
-rw-r--r--crates/ra_hir_ty/src/display.rs44
-rw-r--r--crates/ra_hir_ty/src/infer/pat.rs11
-rw-r--r--crates/ra_hir_ty/src/tests/coercion.rs6
-rw-r--r--crates/ra_hir_ty/src/tests/patterns.rs85
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs3
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs2
-rw-r--r--crates/ra_ide/src/completion.rs16
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_record.rs411
-rw-r--r--crates/ra_ide/src/completion/complete_record_literal.rs241
-rw-r--r--crates/ra_ide/src/completion/complete_record_pattern.rs118
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs8
-rw-r--r--crates/ra_ide/src/completion/presentation.rs12
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs6
-rw-r--r--crates/ra_ide/src/inlay_hints.rs101
-rw-r--r--crates/ra_ide/src/lib.rs17
-rw-r--r--crates/ra_ide/src/ssr.rs116
-rw-r--r--crates/ra_ide_db/src/lib.rs12
-rw-r--r--crates/ra_proc_macro/Cargo.toml5
-rw-r--r--crates/ra_proc_macro/src/lib.rs85
-rw-r--r--crates/ra_proc_macro/src/msg.rs93
-rw-r--r--crates/ra_proc_macro/src/process.rs196
-rw-r--r--crates/ra_proc_macro/src/rpc.rs266
-rw-r--r--crates/ra_prof/src/lib.rs8
-rw-r--r--crates/ra_project_model/Cargo.toml1
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs105
-rw-r--r--crates/ra_project_model/src/lib.rs135
-rw-r--r--crates/ra_syntax/src/ast/edit.rs141
-rw-r--r--crates/ra_syntax/src/ast/expr_extensions.rs5
-rw-r--r--crates/ra_syntax/src/parsing/text_tree_sink.rs13
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0065_comment_newline.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0065_comment_newline.txt17
-rw-r--r--crates/ra_tt/src/lib.rs7
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/bin/main.rs31
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs4
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs15
-rw-r--r--crates/rust-analyzer/src/config.rs261
-rw-r--r--crates/rust-analyzer/src/conv.rs2
-rw-r--r--crates/rust-analyzer/src/feature_flags.rs77
-rw-r--r--crates/rust-analyzer/src/lib.rs4
-rw-r--r--crates/rust-analyzer/src/main_loop.rs243
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs117
-rw-r--r--crates/rust-analyzer/src/main_loop/subscriptions.rs2
-rw-r--r--crates/rust-analyzer/src/req.rs36
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs6
-rw-r--r--crates/rust-analyzer/src/vfs_glob.rs10
-rw-r--r--crates/rust-analyzer/src/world.rs162
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs46
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs55
-rw-r--r--crates/stdx/src/lib.rs2
-rw-r--r--docs/dev/README.md10
-rw-r--r--editors/code/package-lock.json97
-rw-r--r--editors/code/package.json378
-rw-r--r--editors/code/ra_syntax_tree.tmGrammar.json31
-rw-r--r--editors/code/src/client.ts29
-rw-r--r--editors/code/src/commands/runnables.ts4
-rw-r--r--editors/code/src/commands/syntax_tree.ts242
-rw-r--r--editors/code/src/config.ts34
-rw-r--r--editors/code/src/ctx.ts9
-rw-r--r--editors/code/src/highlighting.ts255
-rw-r--r--editors/code/src/inlay_hints.ts8
-rw-r--r--editors/code/src/main.ts22
-rw-r--r--editors/code/src/status_display.ts2
-rw-r--r--editors/code/src/tasks.ts52
-rw-r--r--editors/code/src/util.ts4
86 files changed, 3256 insertions, 2318 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 5050c558c..fb7afe9fd 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -11,6 +11,7 @@ env:
11 CARGO_INCREMENTAL: 0 11 CARGO_INCREMENTAL: 0
12 CARGO_NET_RETRY: 10 12 CARGO_NET_RETRY: 10
13 RUN_SLOW_TESTS: 1 13 RUN_SLOW_TESTS: 1
14 RUST_BACKTRACE: short
14 RUSTFLAGS: -D warnings 15 RUSTFLAGS: -D warnings
15 RUSTUP_MAX_RETRIES: 10 16 RUSTUP_MAX_RETRIES: 10
16 17
diff --git a/Cargo.lock b/Cargo.lock
index 8d81c4839..c07a9614a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -11,9 +11,9 @@ dependencies = [
11 11
12[[package]] 12[[package]]
13name = "anyhow" 13name = "anyhow"
14version = "1.0.27" 14version = "1.0.28"
15source = "registry+https://github.com/rust-lang/crates.io-index" 15source = "registry+https://github.com/rust-lang/crates.io-index"
16checksum = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785" 16checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff"
17 17
18[[package]] 18[[package]]
19name = "anymap" 19name = "anymap"
@@ -331,9 +331,9 @@ dependencies = [
331 331
332[[package]] 332[[package]]
333name = "filetime" 333name = "filetime"
334version = "0.2.8" 334version = "0.2.9"
335source = "registry+https://github.com/rust-lang/crates.io-index" 335source = "registry+https://github.com/rust-lang/crates.io-index"
336checksum = "1ff6d4dab0aa0c8e6346d46052e93b13a16cf847b54ed357087c35011048cc7d" 336checksum = "f59efc38004c988e4201d11d263b8171f49a2e7ec0bdbb71773433f271504a5e"
337dependencies = [ 337dependencies = [
338 "cfg-if", 338 "cfg-if",
339 "libc", 339 "libc",
@@ -435,9 +435,9 @@ dependencies = [
435 435
436[[package]] 436[[package]]
437name = "hermit-abi" 437name = "hermit-abi"
438version = "0.1.8" 438version = "0.1.10"
439source = "registry+https://github.com/rust-lang/crates.io-index" 439source = "registry+https://github.com/rust-lang/crates.io-index"
440checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" 440checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e"
441dependencies = [ 441dependencies = [
442 "libc", 442 "libc",
443] 443]
@@ -563,9 +563,9 @@ dependencies = [
563 563
564[[package]] 564[[package]]
565name = "jod-thread" 565name = "jod-thread"
566version = "0.1.0" 566version = "0.1.1"
567source = "registry+https://github.com/rust-lang/crates.io-index" 567source = "registry+https://github.com/rust-lang/crates.io-index"
568checksum = "2f52a11f73b88fab829a0e4d9e13ea5982c7ac457c72eb3541d82a4afdfce4ff" 568checksum = "4022656272c3e564a7cdebcaaba6518d844b0d0c1836597196efb5bfeb98bb49"
569 569
570[[package]] 570[[package]]
571name = "kernel32-sys" 571name = "kernel32-sys"
@@ -792,9 +792,9 @@ dependencies = [
792 792
793[[package]] 793[[package]]
794name = "paste" 794name = "paste"
795version = "0.1.8" 795version = "0.1.9"
796source = "registry+https://github.com/rust-lang/crates.io-index" 796source = "registry+https://github.com/rust-lang/crates.io-index"
797checksum = "8292c1e1e81ddb552c4c90c36af201a0ce7e34995f55f0480f01052f242811c9" 797checksum = "092d791bf7847f70bbd49085489fba25fc2c193571752bff9e36e74e72403932"
798dependencies = [ 798dependencies = [
799 "paste-impl", 799 "paste-impl",
800 "proc-macro-hack", 800 "proc-macro-hack",
@@ -802,9 +802,9 @@ dependencies = [
802 802
803[[package]] 803[[package]]
804name = "paste-impl" 804name = "paste-impl"
805version = "0.1.8" 805version = "0.1.9"
806source = "registry+https://github.com/rust-lang/crates.io-index" 806source = "registry+https://github.com/rust-lang/crates.io-index"
807checksum = "5e9c43f2645f06ee452544ad032886a75f3d1797b9487dcadcae9100ba58a51c" 807checksum = "406c23fb4c45cc6f68a9bbabb8ec7bd6f8cfcbd17e9e8f72c2460282f8325729"
808dependencies = [ 808dependencies = [
809 "proc-macro-hack", 809 "proc-macro-hack",
810 "proc-macro2", 810 "proc-macro2",
@@ -842,15 +842,15 @@ checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
842 842
843[[package]] 843[[package]]
844name = "proc-macro-hack" 844name = "proc-macro-hack"
845version = "0.5.14" 845version = "0.5.15"
846source = "registry+https://github.com/rust-lang/crates.io-index" 846source = "registry+https://github.com/rust-lang/crates.io-index"
847checksum = "fcfdefadc3d57ca21cf17990a28ef4c0f7c61383a28cb7604cf4a18e6ede1420" 847checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
848 848
849[[package]] 849[[package]]
850name = "proc-macro2" 850name = "proc-macro2"
851version = "1.0.9" 851version = "1.0.10"
852source = "registry+https://github.com/rust-lang/crates.io-index" 852source = "registry+https://github.com/rust-lang/crates.io-index"
853checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" 853checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3"
854dependencies = [ 854dependencies = [
855 "unicode-xid", 855 "unicode-xid",
856] 856]
@@ -887,18 +887,6 @@ dependencies = [
887] 887]
888 888
889[[package]] 889[[package]]
890name = "ra_cargo_watch"
891version = "0.1.0"
892dependencies = [
893 "cargo_metadata",
894 "crossbeam-channel",
895 "insta",
896 "log",
897 "lsp-types",
898 "serde_json",
899]
900
901[[package]]
902name = "ra_cfg" 890name = "ra_cfg"
903version = "0.1.0" 891version = "0.1.0"
904dependencies = [ 892dependencies = [
@@ -923,6 +911,19 @@ dependencies = [
923] 911]
924 912
925[[package]] 913[[package]]
914name = "ra_flycheck"
915version = "0.1.0"
916dependencies = [
917 "cargo_metadata",
918 "crossbeam-channel",
919 "insta",
920 "jod-thread",
921 "log",
922 "lsp-types",
923 "serde_json",
924]
925
926[[package]]
926name = "ra_fmt" 927name = "ra_fmt"
927version = "0.1.0" 928version = "0.1.0"
928dependencies = [ 929dependencies = [
@@ -1074,7 +1075,12 @@ dependencies = [
1074name = "ra_proc_macro" 1075name = "ra_proc_macro"
1075version = "0.1.0" 1076version = "0.1.0"
1076dependencies = [ 1077dependencies = [
1078 "crossbeam-channel",
1079 "jod-thread",
1080 "log",
1077 "ra_tt", 1081 "ra_tt",
1082 "serde",
1083 "serde_json",
1078] 1084]
1079 1085
1080[[package]] 1086[[package]]
@@ -1095,7 +1101,6 @@ dependencies = [
1095 "cargo_metadata", 1101 "cargo_metadata",
1096 "log", 1102 "log",
1097 "ra_arena", 1103 "ra_arena",
1098 "ra_cargo_watch",
1099 "ra_cfg", 1104 "ra_cfg",
1100 "ra_db", 1105 "ra_db",
1101 "ra_proc_macro", 1106 "ra_proc_macro",
@@ -1294,8 +1299,8 @@ dependencies = [
1294 "lsp-types", 1299 "lsp-types",
1295 "parking_lot", 1300 "parking_lot",
1296 "pico-args", 1301 "pico-args",
1297 "ra_cargo_watch",
1298 "ra_db", 1302 "ra_db",
1303 "ra_flycheck",
1299 "ra_hir", 1304 "ra_hir",
1300 "ra_hir_def", 1305 "ra_hir_def",
1301 "ra_hir_ty", 1306 "ra_hir_ty",
@@ -1425,9 +1430,9 @@ dependencies = [
1425 1430
1426[[package]] 1431[[package]]
1427name = "serde_json" 1432name = "serde_json"
1428version = "1.0.48" 1433version = "1.0.50"
1429source = "registry+https://github.com/rust-lang/crates.io-index" 1434source = "registry+https://github.com/rust-lang/crates.io-index"
1430checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" 1435checksum = "78a7a12c167809363ec3bd7329fc0a3369056996de43c4b37ef3cd54a6ce4867"
1431dependencies = [ 1436dependencies = [
1432 "itoa", 1437 "itoa",
1433 "ryu", 1438 "ryu",
@@ -1650,9 +1655,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1650 1655
1651[[package]] 1656[[package]]
1652name = "winapi-util" 1657name = "winapi-util"
1653version = "0.1.3" 1658version = "0.1.4"
1654source = "registry+https://github.com/rust-lang/crates.io-index" 1659source = "registry+https://github.com/rust-lang/crates.io-index"
1655checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" 1660checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e"
1656dependencies = [ 1661dependencies = [
1657 "winapi 0.3.8", 1662 "winapi 0.3.8",
1658] 1663]
diff --git a/crates/ra_arena/src/map.rs b/crates/ra_arena/src/map.rs
index 5e764113d..0f33907c0 100644
--- a/crates/ra_arena/src/map.rs
+++ b/crates/ra_arena/src/map.rs
@@ -14,14 +14,8 @@ pub struct ArenaMap<ID, V> {
14impl<T, V> ArenaMap<Idx<T>, V> { 14impl<T, V> ArenaMap<Idx<T>, V> {
15 pub fn insert(&mut self, id: Idx<T>, t: V) { 15 pub fn insert(&mut self, id: Idx<T>, t: V) {
16 let idx = Self::to_idx(id); 16 let idx = Self::to_idx(id);
17 if self.v.capacity() <= idx { 17
18 self.v.reserve(idx + 1 - self.v.capacity()); 18 self.v.resize_with((idx + 1).max(self.v.len()), || None);
19 }
20 if self.v.len() <= idx {
21 while self.v.len() <= idx {
22 self.v.push(None);
23 }
24 }
25 self.v[idx] = Some(t); 19 self.v[idx] = Some(t);
26 } 20 }
27 21
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index add82e5b1..8d1af9933 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -1,15 +1,11 @@
1//! FIXME: write short doc here
2
3use std::iter; 1use std::iter;
4 2
5use hir::{Adt, HasSource, ModuleDef, Semantics}; 3use hir::{Adt, HasSource, ModuleDef, Semantics};
6use itertools::Itertools; 4use itertools::Itertools;
7use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
8 7
9use crate::{Assist, AssistCtx, AssistId}; 8use crate::{Assist, AssistCtx, AssistId};
10use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner};
11
12use ast::{MatchArm, Pat};
13 9
14// Assist: fill_match_arms 10// Assist: fill_match_arms
15// 11//
@@ -97,10 +93,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
97 } 93 }
98 94
99 ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| { 95 ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| {
100 arms.extend(missing_arms); 96 let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms);
101
102 let indent_level = IndentLevel::from_node(match_arm_list.syntax());
103 let new_arm_list = indent_level.increase_indent(make::match_arm_list(arms));
104 97
105 edit.target(match_expr.syntax().text_range()); 98 edit.target(match_expr.syntax().text_range());
106 edit.set_cursor(expr.syntax().text_range().start()); 99 edit.set_cursor(expr.syntax().text_range().start());
@@ -655,4 +648,93 @@ mod tests {
655 "#, 648 "#,
656 ); 649 );
657 } 650 }
651
652 #[test]
653 fn fill_match_arms_preserves_comments() {
654 check_assist(
655 fill_match_arms,
656 r#"
657 enum A {
658 One,
659 Two,
660 }
661 fn foo(a: A) {
662 match a {
663 // foo bar baz<|>
664 A::One => {}
665 // This is where the rest should be
666 }
667 }
668 "#,
669 r#"
670 enum A {
671 One,
672 Two,
673 }
674 fn foo(a: A) {
675 match <|>a {
676 // foo bar baz
677 A::One => {}
678 // This is where the rest should be
679 A::Two => {}
680 }
681 }
682 "#,
683 );
684 }
685
686 #[test]
687 fn fill_match_arms_preserves_comments_empty() {
688 check_assist(
689 fill_match_arms,
690 r#"
691 enum A {
692 One,
693 Two,
694 }
695 fn foo(a: A) {
696 match a {
697 // foo bar baz<|>
698 }
699 }
700 "#,
701 r#"
702 enum A {
703 One,
704 Two,
705 }
706 fn foo(a: A) {
707 match <|>a {
708 // foo bar baz
709 A::One => {}
710 A::Two => {}
711 }
712 }
713 "#,
714 );
715 }
716
717 #[test]
718 fn fill_match_arms_placeholder() {
719 check_assist(
720 fill_match_arms,
721 r#"
722 enum A { One, Two, }
723 fn foo(a: A) {
724 match a<|> {
725 _ => (),
726 }
727 }
728 "#,
729 r#"
730 enum A { One, Two, }
731 fn foo(a: A) {
732 match <|>a {
733 A::One => {}
734 A::Two => {}
735 }
736 }
737 "#,
738 );
739 }
658} 740}
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs
deleted file mode 100644
index 7c525c430..000000000
--- a/crates/ra_cargo_watch/src/lib.rs
+++ /dev/null
@@ -1,405 +0,0 @@
1//! cargo_check provides the functionality needed to run `cargo check` or
2//! another compatible command (f.x. clippy) in a background thread and provide
3//! LSP diagnostics based on the output of the command.
4use cargo_metadata::Message;
5use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
6use lsp_types::{
7 CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
8 WorkDoneProgressEnd, WorkDoneProgressReport,
9};
10use std::{
11 error, fmt,
12 io::{BufRead, BufReader},
13 path::{Path, PathBuf},
14 process::{Command, Stdio},
15 thread::JoinHandle,
16 time::Instant,
17};
18
19mod conv;
20
21use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};
22
23pub use crate::conv::url_from_path_with_drive_lowercasing;
24
25#[derive(Clone, Debug)]
26pub struct CheckOptions {
27 pub enable: bool,
28 pub args: Vec<String>,
29 pub command: String,
30 pub all_targets: bool,
31}
32
33/// CheckWatcher wraps the shared state and communication machinery used for
34/// running `cargo check` (or other compatible command) and providing
35/// diagnostics based on the output.
36/// The spawned thread is shut down when this struct is dropped.
37#[derive(Debug)]
38pub struct CheckWatcher {
39 pub task_recv: Receiver<CheckTask>,
40 cmd_send: Option<Sender<CheckCommand>>,
41 handle: Option<JoinHandle<()>>,
42}
43
44impl CheckWatcher {
45 pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher {
46 let options = options.clone();
47
48 let (task_send, task_recv) = unbounded::<CheckTask>();
49 let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
50 let handle = std::thread::spawn(move || {
51 let mut check = CheckWatcherThread::new(options, workspace_root);
52 check.run(&task_send, &cmd_recv);
53 });
54 CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle) }
55 }
56
57 /// Returns a CheckWatcher that doesn't actually do anything
58 pub fn dummy() -> CheckWatcher {
59 CheckWatcher { task_recv: never(), cmd_send: None, handle: None }
60 }
61
62 /// Schedule a re-start of the cargo check worker.
63 pub fn update(&self) {
64 if let Some(cmd_send) = &self.cmd_send {
65 cmd_send.send(CheckCommand::Update).unwrap();
66 }
67 }
68}
69
70impl std::ops::Drop for CheckWatcher {
71 fn drop(&mut self) {
72 if let Some(handle) = self.handle.take() {
73 // Take the sender out of the option
74 let cmd_send = self.cmd_send.take();
75
76 // Dropping the sender finishes the thread loop
77 drop(cmd_send);
78
79 // Join the thread, it should finish shortly. We don't really care
80 // whether it panicked, so it is safe to ignore the result
81 let _ = handle.join();
82 }
83 }
84}
85
86#[derive(Debug)]
87pub enum CheckTask {
88 /// Request a clearing of all cached diagnostics from the check watcher
89 ClearDiagnostics,
90
91 /// Request adding a diagnostic with fixes included to a file
92 AddDiagnostic { url: Url, diagnostic: Diagnostic, fixes: Vec<CodeActionOrCommand> },
93
94 /// Request check progress notification to client
95 Status(WorkDoneProgress),
96}
97
98pub enum CheckCommand {
99 /// Request re-start of check thread
100 Update,
101}
102
103struct CheckWatcherThread {
104 options: CheckOptions,
105 workspace_root: PathBuf,
106 watcher: WatchThread,
107 last_update_req: Option<Instant>,
108}
109
110impl CheckWatcherThread {
111 fn new(options: CheckOptions, workspace_root: PathBuf) -> CheckWatcherThread {
112 CheckWatcherThread {
113 options,
114 workspace_root,
115 watcher: WatchThread::dummy(),
116 last_update_req: None,
117 }
118 }
119
120 fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
121 loop {
122 select! {
123 recv(&cmd_recv) -> cmd => match cmd {
124 Ok(cmd) => self.handle_command(cmd),
125 Err(RecvError) => {
126 // Command channel has closed, so shut down
127 break;
128 },
129 },
130 recv(self.watcher.message_recv) -> msg => match msg {
131 Ok(msg) => self.handle_message(msg, task_send),
132 Err(RecvError) => {
133 // Watcher finished, replace it with a never channel to
134 // avoid busy-waiting.
135 std::mem::replace(&mut self.watcher.message_recv, never());
136 },
137 }
138 };
139
140 if self.should_recheck() {
141 self.last_update_req.take();
142 task_send.send(CheckTask::ClearDiagnostics).unwrap();
143
144 // Replace with a dummy watcher first so we drop the original and wait for completion
145 std::mem::replace(&mut self.watcher, WatchThread::dummy());
146
147 // Then create the actual new watcher
148 self.watcher = WatchThread::new(&self.options, &self.workspace_root);
149 }
150 }
151 }
152
153 fn should_recheck(&mut self) -> bool {
154 if let Some(_last_update_req) = &self.last_update_req {
155 // We currently only request an update on save, as we need up to
156 // date source on disk for cargo check to do it's magic, so we
157 // don't really need to debounce the requests at this point.
158 return true;
159 }
160 false
161 }
162
163 fn handle_command(&mut self, cmd: CheckCommand) {
164 match cmd {
165 CheckCommand::Update => self.last_update_req = Some(Instant::now()),
166 }
167 }
168
169 fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
170 match msg {
171 CheckEvent::Begin => {
172 task_send
173 .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
174 title: "Running 'cargo check'".to_string(),
175 cancellable: Some(false),
176 message: None,
177 percentage: None,
178 })))
179 .unwrap();
180 }
181
182 CheckEvent::End => {
183 task_send
184 .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
185 message: None,
186 })))
187 .unwrap();
188 }
189
190 CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
191 task_send
192 .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
193 cancellable: Some(false),
194 message: Some(msg.target.name),
195 percentage: None,
196 })))
197 .unwrap();
198 }
199
200 CheckEvent::Msg(Message::CompilerMessage(msg)) => {
201 let map_result = map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root);
202 if map_result.is_empty() {
203 return;
204 }
205
206 for MappedRustDiagnostic { location, diagnostic, fixes } in map_result {
207 let fixes = fixes
208 .into_iter()
209 .map(|fix| {
210 CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into()
211 })
212 .collect();
213
214 task_send
215 .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes })
216 .unwrap();
217 }
218 }
219
220 CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
221 CheckEvent::Msg(Message::Unknown) => {}
222 }
223 }
224}
225
226#[derive(Debug)]
227pub struct DiagnosticWithFixes {
228 diagnostic: Diagnostic,
229 fixes: Vec<CodeAction>,
230}
231
232/// WatchThread exists to wrap around the communication needed to be able to
233/// run `cargo check` without blocking. Currently the Rust standard library
234/// doesn't provide a way to read sub-process output without blocking, so we
235/// have to wrap sub-processes output handling in a thread and pass messages
236/// back over a channel.
237/// The correct way to dispose of the thread is to drop it, on which the
238/// sub-process will be killed, and the thread will be joined.
239struct WatchThread {
240 handle: Option<JoinHandle<()>>,
241 message_recv: Receiver<CheckEvent>,
242}
243
244enum CheckEvent {
245 Begin,
246 Msg(cargo_metadata::Message),
247 End,
248}
249
250#[derive(Debug)]
251pub struct CargoError(String);
252
253impl fmt::Display for CargoError {
254 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
255 write!(f, "Cargo failed: {}", self.0)
256 }
257}
258impl error::Error for CargoError {}
259
260pub fn run_cargo(
261 args: &[String],
262 current_dir: Option<&Path>,
263 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
264) -> Result<(), CargoError> {
265 let mut command = Command::new("cargo");
266 if let Some(current_dir) = current_dir {
267 command.current_dir(current_dir);
268 }
269
270 let mut child = command
271 .args(args)
272 .stdout(Stdio::piped())
273 .stderr(Stdio::null())
274 .stdin(Stdio::null())
275 .spawn()
276 .expect("couldn't launch cargo");
277
278 // We manually read a line at a time, instead of using serde's
279 // stream deserializers, because the deserializer cannot recover
280 // from an error, resulting in it getting stuck, because we try to
281 // be resillient against failures.
282 //
283 // Because cargo only outputs one JSON object per line, we can
284 // simply skip a line if it doesn't parse, which just ignores any
285 // erroneus output.
286 let stdout = BufReader::new(child.stdout.take().unwrap());
287 let mut read_at_least_one_message = false;
288
289 for line in stdout.lines() {
290 let line = match line {
291 Ok(line) => line,
292 Err(err) => {
293 log::error!("Couldn't read line from cargo: {}", err);
294 continue;
295 }
296 };
297
298 let message = serde_json::from_str::<cargo_metadata::Message>(&line);
299 let message = match message {
300 Ok(message) => message,
301 Err(err) => {
302 log::error!("Invalid json from cargo check, ignoring ({}): {:?} ", err, line);
303 continue;
304 }
305 };
306
307 read_at_least_one_message = true;
308
309 if !on_message(message) {
310 break;
311 }
312 }
313
314 // It is okay to ignore the result, as it only errors if the process is already dead
315 let _ = child.kill();
316
317 let err_msg = match child.wait() {
318 Ok(exit_code) if !exit_code.success() && !read_at_least_one_message => {
319 // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
320 // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
321 format!(
322 "the command produced no valid metadata (exit code: {:?}): cargo {}",
323 exit_code,
324 args.join(" ")
325 )
326 }
327 Err(err) => format!("io error: {:?}", err),
328 Ok(_) => return Ok(()),
329 };
330
331 Err(CargoError(err_msg))
332}
333
334impl WatchThread {
335 fn dummy() -> WatchThread {
336 WatchThread { handle: None, message_recv: never() }
337 }
338
339 fn new(options: &CheckOptions, workspace_root: &Path) -> WatchThread {
340 let mut args: Vec<String> = vec![
341 options.command.clone(),
342 "--workspace".to_string(),
343 "--message-format=json".to_string(),
344 "--manifest-path".to_string(),
345 format!("{}/Cargo.toml", workspace_root.display()),
346 ];
347 if options.all_targets {
348 args.push("--all-targets".to_string());
349 }
350 args.extend(options.args.iter().cloned());
351
352 let (message_send, message_recv) = unbounded();
353 let workspace_root = workspace_root.to_owned();
354 let handle = if options.enable {
355 Some(std::thread::spawn(move || {
356 // If we trigger an error here, we will do so in the loop instead,
357 // which will break out of the loop, and continue the shutdown
358 let _ = message_send.send(CheckEvent::Begin);
359
360 let res = run_cargo(&args, Some(&workspace_root), &mut |message| {
361 // Skip certain kinds of messages to only spend time on what's useful
362 match &message {
363 Message::CompilerArtifact(artifact) if artifact.fresh => return true,
364 Message::BuildScriptExecuted(_) => return true,
365 Message::Unknown => return true,
366 _ => {}
367 }
368
369 // if the send channel was closed, we want to shutdown
370 message_send.send(CheckEvent::Msg(message)).is_ok()
371 });
372
373 if let Err(err) = res {
374 // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
375 // to display user-caused misconfiguration errors instead of just logging them here
376 log::error!("Cargo watcher failed {:?}", err);
377 }
378
379 // We can ignore any error here, as we are already in the progress
380 // of shutting down.
381 let _ = message_send.send(CheckEvent::End);
382 }))
383 } else {
384 None
385 };
386 WatchThread { handle, message_recv }
387 }
388}
389
390impl std::ops::Drop for WatchThread {
391 fn drop(&mut self) {
392 if let Some(handle) = self.handle.take() {
393 // Replace our reciever with dummy one, so we can drop and close the
394 // one actually communicating with the thread
395 let recv = std::mem::replace(&mut self.message_recv, never());
396
397 // Dropping the original reciever initiates thread sub-process shutdown
398 drop(recv);
399
400 // Join the thread, it should finish shortly. We don't really care
401 // whether it panicked, so it is safe to ignore the result
402 let _ = handle.join();
403 }
404 }
405}
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_flycheck/Cargo.toml
index 741345a21..c9a9ddc12 100644
--- a/crates/ra_cargo_watch/Cargo.toml
+++ b/crates/ra_flycheck/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2edition = "2018" 2edition = "2018"
3name = "ra_cargo_watch" 3name = "ra_flycheck"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6 6
@@ -10,6 +10,7 @@ lsp-types = { version = "0.73.0", features = ["proposed"] }
10log = "0.4.8" 10log = "0.4.8"
11cargo_metadata = "0.9.1" 11cargo_metadata = "0.9.1"
12serde_json = "1.0.48" 12serde_json = "1.0.48"
13jod-thread = "0.1.1"
13 14
14[dev-dependencies] 15[dev-dependencies]
15insta = "0.15.0" 16insta = "0.15.0"
diff --git a/crates/ra_cargo_watch/src/conv.rs b/crates/ra_flycheck/src/conv.rs
index 817543deb..817543deb 100644
--- a/crates/ra_cargo_watch/src/conv.rs
+++ b/crates/ra_flycheck/src/conv.rs
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap
index a59fa84fa..4c9db0385 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap
index 61ae0c9ae..7cde4d867 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap
index 641da1a58..1cc37e087 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap
index 0557a2e79..615ed8378 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap
index 754bc33a4..0df0fce18 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap
index 78b7f7cc8..28ebcb3b3 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap
index 5989ed202..5e0873281 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap
index e34b546dc..e500d3cd6 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/test.rs b/crates/ra_flycheck/src/conv/test.rs
index 4e81455ca..4e81455ca 100644
--- a/crates/ra_cargo_watch/src/conv/test.rs
+++ b/crates/ra_flycheck/src/conv/test.rs
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs
new file mode 100644
index 000000000..b54a30ab8
--- /dev/null
+++ b/crates/ra_flycheck/src/lib.rs
@@ -0,0 +1,340 @@
1//! cargo_check provides the functionality needed to run `cargo check` or
2//! another compatible command (f.x. clippy) in a background thread and provide
3//! LSP diagnostics based on the output of the command.
4mod conv;
5
6use std::{
7 env,
8 io::{self, BufRead, BufReader},
9 path::PathBuf,
10 process::{Command, Stdio},
11 time::Instant,
12};
13
14use cargo_metadata::Message;
15use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
16use lsp_types::{
17 CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
18 WorkDoneProgressEnd, WorkDoneProgressReport,
19};
20
21use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};
22
23pub use crate::conv::url_from_path_with_drive_lowercasing;
24
25#[derive(Clone, Debug, PartialEq, Eq)]
26pub enum FlycheckConfig {
27 CargoCommand { command: String, all_targets: bool, extra_args: Vec<String> },
28 CustomCommand { command: String, args: Vec<String> },
29}
30
31/// Flycheck wraps the shared state and communication machinery used for
32/// running `cargo check` (or other compatible command) and providing
33/// diagnostics based on the output.
34/// The spawned thread is shut down when this struct is dropped.
35#[derive(Debug)]
36pub struct Flycheck {
37 // XXX: drop order is significant
38 cmd_send: Sender<CheckCommand>,
39 handle: jod_thread::JoinHandle<()>,
40 pub task_recv: Receiver<CheckTask>,
41}
42
43impl Flycheck {
44 pub fn new(config: FlycheckConfig, workspace_root: PathBuf) -> Flycheck {
45 let (task_send, task_recv) = unbounded::<CheckTask>();
46 let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
47 let handle = jod_thread::spawn(move || {
48 FlycheckThread::new(config, workspace_root).run(&task_send, &cmd_recv);
49 });
50 Flycheck { task_recv, cmd_send, handle }
51 }
52
53 /// Schedule a re-start of the cargo check worker.
54 pub fn update(&self) {
55 self.cmd_send.send(CheckCommand::Update).unwrap();
56 }
57}
58
59#[derive(Debug)]
60pub enum CheckTask {
61 /// Request a clearing of all cached diagnostics from the check watcher
62 ClearDiagnostics,
63
64 /// Request adding a diagnostic with fixes included to a file
65 AddDiagnostic { url: Url, diagnostic: Diagnostic, fixes: Vec<CodeActionOrCommand> },
66
67 /// Request check progress notification to client
68 Status(WorkDoneProgress),
69}
70
71pub enum CheckCommand {
72 /// Request re-start of check thread
73 Update,
74}
75
76struct FlycheckThread {
77 config: FlycheckConfig,
78 workspace_root: PathBuf,
79 last_update_req: Option<Instant>,
80 // XXX: drop order is significant
81 message_recv: Receiver<CheckEvent>,
82 /// WatchThread exists to wrap around the communication needed to be able to
83 /// run `cargo check` without blocking. Currently the Rust standard library
84 /// doesn't provide a way to read sub-process output without blocking, so we
85 /// have to wrap sub-processes output handling in a thread and pass messages
86 /// back over a channel.
87 check_process: Option<jod_thread::JoinHandle<()>>,
88}
89
90impl FlycheckThread {
91 fn new(config: FlycheckConfig, workspace_root: PathBuf) -> FlycheckThread {
92 FlycheckThread {
93 config,
94 workspace_root,
95 last_update_req: None,
96 message_recv: never(),
97 check_process: None,
98 }
99 }
100
101 fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
102 // If we rerun the thread, we need to discard the previous check results first
103 self.clean_previous_results(task_send);
104
105 loop {
106 select! {
107 recv(&cmd_recv) -> cmd => match cmd {
108 Ok(cmd) => self.handle_command(cmd),
109 Err(RecvError) => {
110 // Command channel has closed, so shut down
111 break;
112 },
113 },
114 recv(self.message_recv) -> msg => match msg {
115 Ok(msg) => self.handle_message(msg, task_send),
116 Err(RecvError) => {
117 // Watcher finished, replace it with a never channel to
118 // avoid busy-waiting.
119 self.message_recv = never();
120 self.check_process = None;
121 },
122 }
123 };
124
125 if self.should_recheck() {
126 self.last_update_req = None;
127 task_send.send(CheckTask::ClearDiagnostics).unwrap();
128 self.restart_check_process();
129 }
130 }
131 }
132
133 fn clean_previous_results(&self, task_send: &Sender<CheckTask>) {
134 task_send.send(CheckTask::ClearDiagnostics).unwrap();
135 task_send
136 .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd { message: None })))
137 .unwrap();
138 }
139
140 fn should_recheck(&mut self) -> bool {
141 if let Some(_last_update_req) = &self.last_update_req {
142 // We currently only request an update on save, as we need up to
143 // date source on disk for cargo check to do it's magic, so we
144 // don't really need to debounce the requests at this point.
145 return true;
146 }
147 false
148 }
149
150 fn handle_command(&mut self, cmd: CheckCommand) {
151 match cmd {
152 CheckCommand::Update => self.last_update_req = Some(Instant::now()),
153 }
154 }
155
156 fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
157 match msg {
158 CheckEvent::Begin => {
159 task_send
160 .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
161 title: "Running 'cargo check'".to_string(),
162 cancellable: Some(false),
163 message: None,
164 percentage: None,
165 })))
166 .unwrap();
167 }
168
169 CheckEvent::End => {
170 task_send
171 .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
172 message: None,
173 })))
174 .unwrap();
175 }
176
177 CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
178 task_send
179 .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
180 cancellable: Some(false),
181 message: Some(msg.target.name),
182 percentage: None,
183 })))
184 .unwrap();
185 }
186
187 CheckEvent::Msg(Message::CompilerMessage(msg)) => {
188 let map_result = map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root);
189 if map_result.is_empty() {
190 return;
191 }
192
193 for MappedRustDiagnostic { location, diagnostic, fixes } in map_result {
194 let fixes = fixes
195 .into_iter()
196 .map(|fix| {
197 CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into()
198 })
199 .collect();
200
201 task_send
202 .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes })
203 .unwrap();
204 }
205 }
206
207 CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
208 CheckEvent::Msg(Message::Unknown) => {}
209 }
210 }
211
212 fn restart_check_process(&mut self) {
213 // First, clear and cancel the old thread
214 self.message_recv = never();
215 self.check_process = None;
216
217 let mut cmd = match &self.config {
218 FlycheckConfig::CargoCommand { command, all_targets, extra_args } => {
219 let mut cmd = Command::new(cargo_binary());
220 cmd.arg(command);
221 cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]);
222 cmd.arg(self.workspace_root.join("Cargo.toml"));
223 if *all_targets {
224 cmd.arg("--all-targets");
225 }
226 cmd.args(extra_args);
227 cmd
228 }
229 FlycheckConfig::CustomCommand { command, args } => {
230 let mut cmd = Command::new(command);
231 cmd.args(args);
232 cmd
233 }
234 };
235 cmd.current_dir(&self.workspace_root);
236
237 let (message_send, message_recv) = unbounded();
238 self.message_recv = message_recv;
239 self.check_process = Some(jod_thread::spawn(move || {
240 // If we trigger an error here, we will do so in the loop instead,
241 // which will break out of the loop, and continue the shutdown
242 let _ = message_send.send(CheckEvent::Begin);
243
244 let res = run_cargo(cmd, &mut |message| {
245 // Skip certain kinds of messages to only spend time on what's useful
246 match &message {
247 Message::CompilerArtifact(artifact) if artifact.fresh => return true,
248 Message::BuildScriptExecuted(_) => return true,
249 Message::Unknown => return true,
250 _ => {}
251 }
252
253 // if the send channel was closed, we want to shutdown
254 message_send.send(CheckEvent::Msg(message)).is_ok()
255 });
256
257 if let Err(err) = res {
258 // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
259 // to display user-caused misconfiguration errors instead of just logging them here
260 log::error!("Cargo watcher failed {:?}", err);
261 }
262
263 // We can ignore any error here, as we are already in the progress
264 // of shutting down.
265 let _ = message_send.send(CheckEvent::End);
266 }))
267 }
268}
269
270#[derive(Debug)]
271pub struct DiagnosticWithFixes {
272 diagnostic: Diagnostic,
273 fixes: Vec<CodeAction>,
274}
275
276enum CheckEvent {
277 Begin,
278 Msg(cargo_metadata::Message),
279 End,
280}
281
282fn run_cargo(
283 mut command: Command,
284 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
285) -> io::Result<()> {
286 let mut child =
287 command.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()).spawn()?;
288
289 // We manually read a line at a time, instead of using serde's
290 // stream deserializers, because the deserializer cannot recover
291 // from an error, resulting in it getting stuck, because we try to
292 // be resillient against failures.
293 //
294 // Because cargo only outputs one JSON object per line, we can
295 // simply skip a line if it doesn't parse, which just ignores any
296 // erroneus output.
297 let stdout = BufReader::new(child.stdout.take().unwrap());
298 let mut read_at_least_one_message = false;
299
300 for line in stdout.lines() {
301 let line = line?;
302
303 let message = serde_json::from_str::<cargo_metadata::Message>(&line);
304 let message = match message {
305 Ok(message) => message,
306 Err(err) => {
307 log::error!("Invalid json from cargo check, ignoring ({}): {:?} ", err, line);
308 continue;
309 }
310 };
311
312 read_at_least_one_message = true;
313
314 if !on_message(message) {
315 break;
316 }
317 }
318
319 // It is okay to ignore the result, as it only errors if the process is already dead
320 let _ = child.kill();
321
322 let exit_status = child.wait()?;
323 if !exit_status.success() && !read_at_least_one_message {
324 // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
325 // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
326 return Err(io::Error::new(
327 io::ErrorKind::Other,
328 format!(
329 "the command produced no valid metadata (exit code: {:?}): {:?}",
330 exit_status, command
331 ),
332 ));
333 }
334
335 Ok(())
336}
337
338fn cargo_binary() -> String {
339 env::var("CARGO").unwrap_or_else(|_| "cargo".to_string())
340}
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index e8443dde8..28c570c76 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -417,26 +417,7 @@ impl ExprCollector<'_> {
417 } 417 }
418 } 418 }
419 419
420 ast::Expr::Literal(e) => { 420 ast::Expr::Literal(e) => self.alloc_expr(Expr::Literal(e.kind().into()), syntax_ptr),
421 let lit = match e.kind() {
422 LiteralKind::IntNumber { suffix } => {
423 let known_name = suffix.and_then(|it| BuiltinInt::from_suffix(&it));
424
425 Literal::Int(Default::default(), known_name)
426 }
427 LiteralKind::FloatNumber { suffix } => {
428 let known_name = suffix.and_then(|it| BuiltinFloat::from_suffix(&it));
429
430 Literal::Float(Default::default(), known_name)
431 }
432 LiteralKind::ByteString => Literal::ByteString(Default::default()),
433 LiteralKind::String => Literal::String(Default::default()),
434 LiteralKind::Byte => Literal::Int(Default::default(), Some(BuiltinInt::U8)),
435 LiteralKind::Bool => Literal::Bool(Default::default()),
436 LiteralKind::Char => Literal::Char(Default::default()),
437 };
438 self.alloc_expr(Expr::Literal(lit), syntax_ptr)
439 }
440 ast::Expr::IndexExpr(e) => { 421 ast::Expr::IndexExpr(e) => {
441 let base = self.collect_expr_opt(e.base()); 422 let base = self.collect_expr_opt(e.base());
442 let index = self.collect_expr_opt(e.index()); 423 let index = self.collect_expr_opt(e.index());
@@ -679,10 +660,19 @@ impl ExprCollector<'_> {
679 suffix: suffix.into_iter().map(|p| self.collect_pat(p)).collect(), 660 suffix: suffix.into_iter().map(|p| self.collect_pat(p)).collect(),
680 } 661 }
681 } 662 }
663 ast::Pat::LiteralPat(lit) => {
664 if let Some(ast_lit) = lit.literal() {
665 let expr = Expr::Literal(ast_lit.kind().into());
666 let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
667 let expr_id = self.alloc_expr(expr, expr_ptr);
668 Pat::Lit(expr_id)
669 } else {
670 Pat::Missing
671 }
672 }
682 673
683 // FIXME: implement 674 // FIXME: implement
684 ast::Pat::BoxPat(_) => Pat::Missing, 675 ast::Pat::BoxPat(_) => Pat::Missing,
685 ast::Pat::LiteralPat(_) => Pat::Missing,
686 ast::Pat::RangePat(_) => Pat::Missing, 676 ast::Pat::RangePat(_) => Pat::Missing,
687 }; 677 };
688 let ptr = AstPtr::new(&pat); 678 let ptr = AstPtr::new(&pat);
@@ -741,3 +731,25 @@ impl From<ast::BinOp> for BinaryOp {
741 } 731 }
742 } 732 }
743} 733}
734
735impl From<ast::LiteralKind> for Literal {
736 fn from(ast_lit_kind: ast::LiteralKind) -> Self {
737 match ast_lit_kind {
738 LiteralKind::IntNumber { suffix } => {
739 let known_name = suffix.and_then(|it| BuiltinInt::from_suffix(&it));
740
741 Literal::Int(Default::default(), known_name)
742 }
743 LiteralKind::FloatNumber { suffix } => {
744 let known_name = suffix.and_then(|it| BuiltinFloat::from_suffix(&it));
745
746 Literal::Float(Default::default(), known_name)
747 }
748 LiteralKind::ByteString => Literal::ByteString(Default::default()),
749 LiteralKind::String => Literal::String(Default::default()),
750 LiteralKind::Byte => Literal::Int(Default::default(), Some(BuiltinInt::U8)),
751 LiteralKind::Bool(val) => Literal::Bool(val),
752 LiteralKind::Char => Literal::Char(Default::default()),
753 }
754 }
755}
diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs
index 4d270e0de..97d1208ec 100644
--- a/crates/ra_hir_expand/src/proc_macro.rs
+++ b/crates/ra_hir_expand/src/proc_macro.rs
@@ -9,6 +9,15 @@ pub struct ProcMacroExpander {
9 proc_macro_id: ProcMacroId, 9 proc_macro_id: ProcMacroId,
10} 10}
11 11
12macro_rules! err {
13 ($fmt:literal, $($tt:tt),*) => {
14 mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown(format!($fmt, $($tt),*)))
15 };
16 ($fmt:literal) => {
17 mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown($fmt.to_string()))
18 }
19}
20
12impl ProcMacroExpander { 21impl ProcMacroExpander {
13 pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander { 22 pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander {
14 ProcMacroExpander { krate, proc_macro_id } 23 ProcMacroExpander { krate, proc_macro_id }
@@ -25,8 +34,24 @@ impl ProcMacroExpander {
25 .proc_macro 34 .proc_macro
26 .get(self.proc_macro_id.0 as usize) 35 .get(self.proc_macro_id.0 as usize)
27 .clone() 36 .clone()
28 .ok_or_else(|| mbe::ExpandError::ConversionError)?; 37 .ok_or_else(|| err!("No derive macro found."))?;
38
39 let tt = remove_derive_atr(tt, &proc_macro.name)
40 .ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
29 41
30 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) 42 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
31 } 43 }
32} 44}
45
46fn remove_derive_atr(tt: &tt::Subtree, _name: &str) -> Option<tt::Subtree> {
47 // FIXME: proper handle the remove derive
48 // We assume the first 2 tokens are #[derive(name)]
49 if tt.token_trees.len() > 2 {
50 let mut tt = tt.clone();
51 tt.token_trees.remove(0);
52 tt.token_trees.remove(0);
53 return Some(tt);
54 }
55
56 None
57}
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs
index a6ef44a31..13ecd537a 100644
--- a/crates/ra_hir_ty/src/display.rs
+++ b/crates/ra_hir_ty/src/display.rs
@@ -159,20 +159,13 @@ impl HirDisplay for ApplicationTy {
159 } 159 }
160 TypeCtor::FnDef(def) => { 160 TypeCtor::FnDef(def) => {
161 let sig = f.db.callable_item_signature(def).subst(&self.parameters); 161 let sig = f.db.callable_item_signature(def).subst(&self.parameters);
162 let name = match def { 162 match def {
163 CallableDef::FunctionId(ff) => f.db.function_data(ff).name.clone(), 163 CallableDef::FunctionId(ff) => write!(f, "fn {}", f.db.function_data(ff).name)?,
164 CallableDef::StructId(s) => f.db.struct_data(s).name.clone(), 164 CallableDef::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?,
165 CallableDef::EnumVariantId(e) => { 165 CallableDef::EnumVariantId(e) => {
166 let enum_data = f.db.enum_data(e.parent); 166 write!(f, "{}", f.db.enum_data(e.parent).variants[e.local_id].name)?
167 enum_data.variants[e.local_id].name.clone()
168 } 167 }
169 }; 168 };
170 match def {
171 CallableDef::FunctionId(_) => write!(f, "fn {}", name)?,
172 CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => {
173 write!(f, "{}", name)?
174 }
175 }
176 if self.parameters.len() > 0 { 169 if self.parameters.len() > 0 {
177 let generics = generics(f.db.upcast(), def.into()); 170 let generics = generics(f.db.upcast(), def.into());
178 let (parent_params, self_param, type_params, _impl_trait_params) = 171 let (parent_params, self_param, type_params, _impl_trait_params) =
@@ -197,8 +190,6 @@ impl HirDisplay for ApplicationTy {
197 }; 190 };
198 write!(f, "{}", name)?; 191 write!(f, "{}", name)?;
199 if self.parameters.len() > 0 { 192 if self.parameters.len() > 0 {
200 write!(f, "<")?;
201
202 let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); 193 let mut non_default_parameters = Vec::with_capacity(self.parameters.len());
203 let parameters_to_write = if f.omit_verbose_types() { 194 let parameters_to_write = if f.omit_verbose_types() {
204 match self 195 match self
@@ -207,8 +198,8 @@ impl HirDisplay for ApplicationTy {
207 .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) 198 .map(|generic_def_id| f.db.generic_defaults(generic_def_id))
208 .filter(|defaults| !defaults.is_empty()) 199 .filter(|defaults| !defaults.is_empty())
209 { 200 {
210 Option::None => self.parameters.0.as_ref(), 201 None => self.parameters.0.as_ref(),
211 Option::Some(default_parameters) => { 202 Some(default_parameters) => {
212 for (i, parameter) in self.parameters.iter().enumerate() { 203 for (i, parameter) in self.parameters.iter().enumerate() {
213 match (parameter, default_parameters.get(i)) { 204 match (parameter, default_parameters.get(i)) {
214 (&Ty::Unknown, _) | (_, None) => { 205 (&Ty::Unknown, _) | (_, None) => {
@@ -228,7 +219,7 @@ impl HirDisplay for ApplicationTy {
228 } else { 219 } else {
229 self.parameters.0.as_ref() 220 self.parameters.0.as_ref()
230 }; 221 };
231 222 write!(f, "<")?;
232 f.write_joined(parameters_to_write, ", ")?; 223 f.write_joined(parameters_to_write, ", ")?;
233 write!(f, ">")?; 224 write!(f, ">")?;
234 } 225 }
@@ -238,9 +229,9 @@ impl HirDisplay for ApplicationTy {
238 AssocContainerId::TraitId(it) => it, 229 AssocContainerId::TraitId(it) => it,
239 _ => panic!("not an associated type"), 230 _ => panic!("not an associated type"),
240 }; 231 };
241 let trait_name = f.db.trait_data(trait_).name.clone(); 232 let trait_ = f.db.trait_data(trait_);
242 let name = f.db.type_alias_data(type_alias).name.clone(); 233 let type_alias = f.db.type_alias_data(type_alias);
243 write!(f, "{}::{}", trait_name, name)?; 234 write!(f, "{}::{}", trait_.name, type_alias.name)?;
244 if self.parameters.len() > 0 { 235 if self.parameters.len() > 0 {
245 write!(f, "<")?; 236 write!(f, "<")?;
246 f.write_joined(&*self.parameters.0, ", ")?; 237 f.write_joined(&*self.parameters.0, ", ")?;
@@ -273,8 +264,8 @@ impl HirDisplay for ProjectionTy {
273 return write!(f, "{}", TYPE_HINT_TRUNCATION); 264 return write!(f, "{}", TYPE_HINT_TRUNCATION);
274 } 265 }
275 266
276 let trait_name = f.db.trait_data(self.trait_(f.db)).name.clone(); 267 let trait_ = f.db.trait_data(self.trait_(f.db));
277 write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_name,)?; 268 write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_.name)?;
278 if self.parameters.len() > 1 { 269 if self.parameters.len() > 1 {
279 write!(f, "<")?; 270 write!(f, "<")?;
280 f.write_joined(&self.parameters[1..], ", ")?; 271 f.write_joined(&self.parameters[1..], ", ")?;
@@ -319,7 +310,7 @@ impl HirDisplay for Ty {
319 Ty::Opaque(_) => write!(f, "impl ")?, 310 Ty::Opaque(_) => write!(f, "impl ")?,
320 _ => unreachable!(), 311 _ => unreachable!(),
321 }; 312 };
322 write_bounds_like_dyn_trait(&predicates, f)?; 313 write_bounds_like_dyn_trait(predicates, f)?;
323 } 314 }
324 Ty::Unknown => write!(f, "{{unknown}}")?, 315 Ty::Unknown => write!(f, "{{unknown}}")?,
325 Ty::Infer(..) => write!(f, "_")?, 316 Ty::Infer(..) => write!(f, "_")?,
@@ -352,7 +343,7 @@ fn write_bounds_like_dyn_trait(
352 // We assume that the self type is $0 (i.e. the 343 // We assume that the self type is $0 (i.e. the
353 // existential) here, which is the only thing that's 344 // existential) here, which is the only thing that's
354 // possible in actual Rust, and hence don't print it 345 // possible in actual Rust, and hence don't print it
355 write!(f, "{}", f.db.trait_data(trait_ref.trait_).name.clone())?; 346 write!(f, "{}", f.db.trait_data(trait_ref.trait_).name)?;
356 if trait_ref.substs.len() > 1 { 347 if trait_ref.substs.len() > 1 {
357 write!(f, "<")?; 348 write!(f, "<")?;
358 f.write_joined(&trait_ref.substs[1..], ", ")?; 349 f.write_joined(&trait_ref.substs[1..], ", ")?;
@@ -369,9 +360,8 @@ fn write_bounds_like_dyn_trait(
369 write!(f, "<")?; 360 write!(f, "<")?;
370 angle_open = true; 361 angle_open = true;
371 } 362 }
372 let name = 363 let type_alias = f.db.type_alias_data(projection_pred.projection_ty.associated_ty);
373 f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name.clone(); 364 write!(f, "{} = ", type_alias.name)?;
374 write!(f, "{} = ", name)?;
375 projection_pred.ty.hir_fmt(f)?; 365 projection_pred.ty.hir_fmt(f)?;
376 } 366 }
377 GenericPredicate::Error => { 367 GenericPredicate::Error => {
@@ -405,7 +395,7 @@ impl TraitRef {
405 } else { 395 } else {
406 write!(f, ": ")?; 396 write!(f, ": ")?;
407 } 397 }
408 write!(f, "{}", f.db.trait_data(self.trait_).name.clone())?; 398 write!(f, "{}", f.db.trait_data(self.trait_).name)?;
409 if self.substs.len() > 1 { 399 if self.substs.len() > 1 {
410 write!(f, "<")?; 400 write!(f, "<")?;
411 f.write_joined(&self.substs[1..], ", ")?; 401 f.write_joined(&self.substs[1..], ", ")?;
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs
index baed6225b..86acd27f8 100644
--- a/crates/ra_hir_ty/src/infer/pat.rs
+++ b/crates/ra_hir_ty/src/infer/pat.rs
@@ -11,7 +11,7 @@ use hir_def::{
11use hir_expand::name::Name; 11use hir_expand::name::Name;
12use test_utils::tested_by; 12use test_utils::tested_by;
13 13
14use super::{BindingMode, InferenceContext}; 14use super::{BindingMode, Expectation, InferenceContext};
15use crate::{utils::variant_data, Substs, Ty, TypeCtor}; 15use crate::{utils::variant_data, Substs, Ty, TypeCtor};
16 16
17impl<'a> InferenceContext<'a> { 17impl<'a> InferenceContext<'a> {
@@ -198,7 +198,14 @@ impl<'a> InferenceContext<'a> {
198 198
199 Ty::apply_one(container_ty, elem_ty) 199 Ty::apply_one(container_ty, elem_ty)
200 } 200 }
201 _ => Ty::Unknown, 201 Pat::Wild => expected.clone(),
202 Pat::Range { start, end } => {
203 let start_ty = self.infer_expr(*start, &Expectation::has_type(expected.clone()));
204 let end_ty = self.infer_expr(*end, &Expectation::has_type(start_ty));
205 end_ty
206 }
207 Pat::Lit(expr) => self.infer_expr(*expr, &Expectation::has_type(expected.clone())),
208 Pat::Missing => Ty::Unknown,
202 }; 209 };
203 // use a new type variable if we got Ty::Unknown here 210 // use a new type variable if we got Ty::Unknown here
204 let ty = self.insert_type_vars_shallow(ty); 211 let ty = self.insert_type_vars_shallow(ty);
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs
index 1e303f5ce..3e3d55c04 100644
--- a/crates/ra_hir_ty/src/tests/coercion.rs
+++ b/crates/ra_hir_ty/src/tests/coercion.rs
@@ -275,12 +275,14 @@ fn test(i: i32) {
275 [70; 147) 'match ... }': &[i32] 275 [70; 147) 'match ... }': &[i32]
276 [76; 77) 'i': i32 276 [76; 77) 'i': i32
277 [88; 89) '2': i32 277 [88; 89) '2': i32
278 [88; 89) '2': i32
278 [93; 96) 'foo': fn foo<i32>(&[i32]) -> &[i32] 279 [93; 96) 'foo': fn foo<i32>(&[i32]) -> &[i32]
279 [93; 102) 'foo(&[2])': &[i32] 280 [93; 102) 'foo(&[2])': &[i32]
280 [97; 101) '&[2]': &[i32; _] 281 [97; 101) '&[2]': &[i32; _]
281 [98; 101) '[2]': [i32; _] 282 [98; 101) '[2]': [i32; _]
282 [99; 100) '2': i32 283 [99; 100) '2': i32
283 [112; 113) '1': i32 284 [112; 113) '1': i32
285 [112; 113) '1': i32
284 [117; 121) '&[1]': &[i32; _] 286 [117; 121) '&[1]': &[i32; _]
285 [118; 121) '[1]': [i32; _] 287 [118; 121) '[1]': [i32; _]
286 [119; 120) '1': i32 288 [119; 120) '1': i32
@@ -316,10 +318,12 @@ fn test(i: i32) {
316 [70; 147) 'match ... }': &[i32] 318 [70; 147) 'match ... }': &[i32]
317 [76; 77) 'i': i32 319 [76; 77) 'i': i32
318 [88; 89) '1': i32 320 [88; 89) '1': i32
321 [88; 89) '1': i32
319 [93; 97) '&[1]': &[i32; _] 322 [93; 97) '&[1]': &[i32; _]
320 [94; 97) '[1]': [i32; _] 323 [94; 97) '[1]': [i32; _]
321 [95; 96) '1': i32 324 [95; 96) '1': i32
322 [107; 108) '2': i32 325 [107; 108) '2': i32
326 [107; 108) '2': i32
323 [112; 115) 'foo': fn foo<i32>(&[i32]) -> &[i32] 327 [112; 115) 'foo': fn foo<i32>(&[i32]) -> &[i32]
324 [112; 121) 'foo(&[2])': &[i32] 328 [112; 121) 'foo(&[2])': &[i32]
325 [116; 120) '&[2]': &[i32; _] 329 [116; 120) '&[2]': &[i32; _]
@@ -357,9 +361,11 @@ fn test() {
357 [45; 142) 'match ... }': *const i32 361 [45; 142) 'match ... }': *const i32
358 [51; 52) '1': i32 362 [51; 52) '1': i32
359 [63; 64) '1': i32 363 [63; 64) '1': i32
364 [63; 64) '1': i32
360 [68; 69) 't': &mut i32 365 [68; 69) 't': &mut i32
361 [68; 81) 't as *mut i32': *mut i32 366 [68; 81) 't as *mut i32': *mut i32
362 [91; 92) '2': i32 367 [91; 92) '2': i32
368 [91; 92) '2': i32
363 [96; 97) 't': &mut i32 369 [96; 97) 't': &mut i32
364 [96; 105) 't as &i32': &i32 370 [96; 105) 't as &i32': &i32
365 [115; 116) '_': i32 371 [115; 116) '_': i32
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs
index 76aa32024..6e5d2247c 100644
--- a/crates/ra_hir_ty/src/tests/patterns.rs
+++ b/crates/ra_hir_ty/src/tests/patterns.rs
@@ -82,6 +82,90 @@ fn test(x: &i32) {
82} 82}
83 83
84#[test] 84#[test]
85fn infer_literal_pattern() {
86 assert_snapshot!(
87 infer_with_mismatches(r#"
88fn any<T>() -> T { loop {} }
89fn test(x: &i32) {
90 if let "foo" = any() {}
91 if let 1 = any() {}
92 if let 1u32 = any() {}
93 if let 1f32 = any() {}
94 if let 1.0 = any() {}
95 if let true = any() {}
96}
97"#, true),
98 @r###"
99 [18; 29) '{ loop {} }': T
100 [20; 27) 'loop {}': !
101 [25; 27) '{}': ()
102 [38; 39) 'x': &i32
103 [47; 209) '{ ...) {} }': ()
104 [53; 76) 'if let...y() {}': ()
105 [60; 65) '"foo"': &str
106 [60; 65) '"foo"': &str
107 [68; 71) 'any': fn any<&str>() -> &str
108 [68; 73) 'any()': &str
109 [74; 76) '{}': ()
110 [81; 100) 'if let...y() {}': ()
111 [88; 89) '1': i32
112 [88; 89) '1': i32
113 [92; 95) 'any': fn any<i32>() -> i32
114 [92; 97) 'any()': i32
115 [98; 100) '{}': ()
116 [105; 127) 'if let...y() {}': ()
117 [112; 116) '1u32': u32
118 [112; 116) '1u32': u32
119 [119; 122) 'any': fn any<u32>() -> u32
120 [119; 124) 'any()': u32
121 [125; 127) '{}': ()
122 [132; 154) 'if let...y() {}': ()
123 [139; 143) '1f32': f32
124 [139; 143) '1f32': f32
125 [146; 149) 'any': fn any<f32>() -> f32
126 [146; 151) 'any()': f32
127 [152; 154) '{}': ()
128 [159; 180) 'if let...y() {}': ()
129 [166; 169) '1.0': f64
130 [166; 169) '1.0': f64
131 [172; 175) 'any': fn any<f64>() -> f64
132 [172; 177) 'any()': f64
133 [178; 180) '{}': ()
134 [185; 207) 'if let...y() {}': ()
135 [192; 196) 'true': bool
136 [192; 196) 'true': bool
137 [199; 202) 'any': fn any<bool>() -> bool
138 [199; 204) 'any()': bool
139 [205; 207) '{}': ()
140 "###
141 );
142}
143
144#[test]
145fn infer_range_pattern() {
146 assert_snapshot!(
147 infer_with_mismatches(r#"
148fn test(x: &i32) {
149 if let 1..76 = 2u32 {}
150 if let 1..=76 = 2u32 {}
151}
152"#, true),
153 @r###"
154 [9; 10) 'x': &i32
155 [18; 76) '{ ...2 {} }': ()
156 [24; 46) 'if let...u32 {}': ()
157 [31; 36) '1..76': u32
158 [39; 43) '2u32': u32
159 [44; 46) '{}': ()
160 [51; 74) 'if let...u32 {}': ()
161 [58; 64) '1..=76': u32
162 [67; 71) '2u32': u32
163 [72; 74) '{}': ()
164 "###
165 );
166}
167
168#[test]
85fn infer_pattern_match_ergonomics() { 169fn infer_pattern_match_ergonomics() {
86 assert_snapshot!( 170 assert_snapshot!(
87 infer(r#" 171 infer(r#"
@@ -212,6 +296,7 @@ fn test() {
212 [59; 62) 'arr': [f64; _] 296 [59; 62) 'arr': [f64; _]
213 [73; 81) '[1.0, a]': [f64; _] 297 [73; 81) '[1.0, a]': [f64; _]
214 [74; 77) '1.0': f64 298 [74; 77) '1.0': f64
299 [74; 77) '1.0': f64
215 [79; 80) 'a': f64 300 [79; 80) 'a': f64
216 [85; 111) '{ ... }': () 301 [85; 111) '{ ... }': ()
217 [99; 100) 'a': f64 302 [99; 100) 'a': f64
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index a02e3ee05..2ee9b8f10 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -206,7 +206,8 @@ pub fn compute() {
206 [24; 106) 'match ... }': () 206 [24; 106) 'match ... }': ()
207 [30; 37) 'nope!()': {unknown} 207 [30; 37) 'nope!()': {unknown}
208 [48; 94) 'SizeSk...tail }': {unknown} 208 [48; 94) 'SizeSk...tail }': {unknown}
209 [82; 86) 'true': {unknown} 209 [82; 86) 'true': bool
210 [82; 86) 'true': bool
210 [88; 92) 'tail': {unknown} 211 [88; 92) 'tail': {unknown}
211 [98; 100) '{}': () 212 [98; 100) '{}': ()
212 "### 213 "###
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index c140bd513..a600b947d 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -948,6 +948,7 @@ fn foo() {
948 [165; 247) 'match ... }': i32 948 [165; 247) 'match ... }': i32
949 [171; 175) 'true': bool 949 [171; 175) 'true': bool
950 [186; 190) 'true': bool 950 [186; 190) 'true': bool
951 [186; 190) 'true': bool
951 [194; 195) '3': i32 952 [194; 195) '3': i32
952 [205; 206) '_': bool 953 [205; 206) '_': bool
953 [210; 241) '{ ... }': ! 954 [210; 241) '{ ... }': !
@@ -956,6 +957,7 @@ fn foo() {
956 [263; 320) 'match ... }': i32 957 [263; 320) 'match ... }': i32
957 [269; 273) 'true': bool 958 [269; 273) 'true': bool
958 [284; 288) 'true': bool 959 [284; 288) 'true': bool
960 [284; 288) 'true': bool
959 [292; 293) '4': i32 961 [292; 293) '4': i32
960 [303; 304) '_': bool 962 [303; 304) '_': bool
961 [308; 314) 'return': ! 963 [308; 314) 'return': !
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index cd0757be5..93157bbba 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -5,8 +5,7 @@ mod completion_context;
5mod presentation; 5mod presentation;
6 6
7mod complete_dot; 7mod complete_dot;
8mod complete_record_literal; 8mod complete_record;
9mod complete_record_pattern;
10mod complete_pattern; 9mod complete_pattern;
11mod complete_fn_param; 10mod complete_fn_param;
12mod complete_keyword; 11mod complete_keyword;
@@ -34,15 +33,15 @@ pub use crate::completion::completion_item::{
34}; 33};
35 34
36#[derive(Clone, Debug, PartialEq, Eq)] 35#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct CompletionOptions { 36pub struct CompletionConfig {
38 pub enable_postfix_completions: bool, 37 pub enable_postfix_completions: bool,
39 pub add_call_parenthesis: bool, 38 pub add_call_parenthesis: bool,
40 pub add_call_argument_snippets: bool, 39 pub add_call_argument_snippets: bool,
41} 40}
42 41
43impl Default for CompletionOptions { 42impl Default for CompletionConfig {
44 fn default() -> Self { 43 fn default() -> Self {
45 CompletionOptions { 44 CompletionConfig {
46 enable_postfix_completions: true, 45 enable_postfix_completions: true,
47 add_call_parenthesis: true, 46 add_call_parenthesis: true,
48 add_call_argument_snippets: true, 47 add_call_argument_snippets: true,
@@ -75,9 +74,9 @@ impl Default for CompletionOptions {
75pub(crate) fn completions( 74pub(crate) fn completions(
76 db: &RootDatabase, 75 db: &RootDatabase,
77 position: FilePosition, 76 position: FilePosition,
78 options: &CompletionOptions, 77 config: &CompletionConfig,
79) -> Option<Completions> { 78) -> Option<Completions> {
80 let ctx = CompletionContext::new(db, position, options)?; 79 let ctx = CompletionContext::new(db, position, config)?;
81 80
82 let mut acc = Completions::default(); 81 let mut acc = Completions::default();
83 82
@@ -89,8 +88,7 @@ pub(crate) fn completions(
89 complete_path::complete_path(&mut acc, &ctx); 88 complete_path::complete_path(&mut acc, &ctx);
90 complete_scope::complete_scope(&mut acc, &ctx); 89 complete_scope::complete_scope(&mut acc, &ctx);
91 complete_dot::complete_dot(&mut acc, &ctx); 90 complete_dot::complete_dot(&mut acc, &ctx);
92 complete_record_literal::complete_record_literal(&mut acc, &ctx); 91 complete_record::complete_record(&mut acc, &ctx);
93 complete_record_pattern::complete_record_pattern(&mut acc, &ctx);
94 complete_pattern::complete_pattern(&mut acc, &ctx); 92 complete_pattern::complete_pattern(&mut acc, &ctx);
95 complete_postfix::complete_postfix(&mut acc, &ctx); 93 complete_postfix::complete_postfix(&mut acc, &ctx);
96 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); 94 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 0a00054b2..29c2881c6 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -15,7 +15,7 @@ use crate::{
15}; 15};
16 16
17pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 17pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
18 if !ctx.options.enable_postfix_completions { 18 if !ctx.config.enable_postfix_completions {
19 return; 19 return;
20 } 20 }
21 21
diff --git a/crates/ra_ide/src/completion/complete_record.rs b/crates/ra_ide/src/completion/complete_record.rs
new file mode 100644
index 000000000..01dd8c6db
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_record.rs
@@ -0,0 +1,411 @@
1//! Complete fields in record literals and patterns.
2use crate::completion::{CompletionContext, Completions};
3use ra_syntax::{ast, ast::NameOwner, SmolStr};
4
5pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
6 let (ty, variant, already_present_fields) =
7 match (ctx.record_lit_pat.as_ref(), ctx.record_lit_syntax.as_ref()) {
8 (None, None) => return None,
9 (Some(_), Some(_)) => panic!("A record cannot be both a literal and a pattern"),
10 (Some(record_pat), _) => (
11 ctx.sema.type_of_pat(&record_pat.clone().into())?,
12 ctx.sema.resolve_record_pattern(record_pat)?,
13 pattern_ascribed_fields(record_pat),
14 ),
15 (_, Some(record_lit)) => (
16 ctx.sema.type_of_expr(&record_lit.clone().into())?,
17 ctx.sema.resolve_record_literal(record_lit)?,
18 literal_ascribed_fields(record_lit),
19 ),
20 };
21
22 for (field, field_ty) in ty.variant_fields(ctx.db, variant).into_iter().filter(|(field, _)| {
23 // FIXME: already_present_names better be `Vec<hir::Name>`
24 !already_present_fields.contains(&SmolStr::from(field.name(ctx.db).to_string()))
25 }) {
26 acc.add_field(ctx, field, &field_ty);
27 }
28 Some(())
29}
30
31fn literal_ascribed_fields(record_lit: &ast::RecordLit) -> Vec<SmolStr> {
32 record_lit
33 .record_field_list()
34 .map(|field_list| field_list.fields())
35 .map(|fields| {
36 fields
37 .into_iter()
38 .filter_map(|field| field.name_ref())
39 .map(|name_ref| name_ref.text().clone())
40 .collect()
41 })
42 .unwrap_or_default()
43}
44
45fn pattern_ascribed_fields(record_pat: &ast::RecordPat) -> Vec<SmolStr> {
46 record_pat
47 .record_field_pat_list()
48 .map(|pat_list| {
49 pat_list
50 .record_field_pats()
51 .filter_map(|fild_pat| fild_pat.name())
52 .chain(pat_list.bind_pats().filter_map(|bind_pat| bind_pat.name()))
53 .map(|name| name.text().clone())
54 .collect()
55 })
56 .unwrap_or_default()
57}
58
59#[cfg(test)]
60mod tests {
61 mod record_lit_tests {
62 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
63 use insta::assert_debug_snapshot;
64
65 fn complete(code: &str) -> Vec<CompletionItem> {
66 do_completion(code, CompletionKind::Reference)
67 }
68
69 #[test]
70 fn test_record_pattern_field() {
71 let completions = complete(
72 r"
73 struct S { foo: u32 }
74
75 fn process(f: S) {
76 match f {
77 S { f<|>: 92 } => (),
78 }
79 }
80 ",
81 );
82 assert_debug_snapshot!(completions, @r###"
83 [
84 CompletionItem {
85 label: "foo",
86 source_range: [117; 118),
87 delete: [117; 118),
88 insert: "foo",
89 kind: Field,
90 detail: "u32",
91 },
92 ]
93 "###);
94 }
95
96 #[test]
97 fn test_record_pattern_enum_variant() {
98 let completions = complete(
99 r"
100 enum E {
101 S { foo: u32, bar: () }
102 }
103
104 fn process(e: E) {
105 match e {
106 E::S { <|> } => (),
107 }
108 }
109 ",
110 );
111 assert_debug_snapshot!(completions, @r###"
112 [
113 CompletionItem {
114 label: "bar",
115 source_range: [161; 161),
116 delete: [161; 161),
117 insert: "bar",
118 kind: Field,
119 detail: "()",
120 },
121 CompletionItem {
122 label: "foo",
123 source_range: [161; 161),
124 delete: [161; 161),
125 insert: "foo",
126 kind: Field,
127 detail: "u32",
128 },
129 ]
130 "###);
131 }
132
133 #[test]
134 fn test_record_pattern_field_in_simple_macro() {
135 let completions = complete(
136 r"
137 macro_rules! m { ($e:expr) => { $e } }
138 struct S { foo: u32 }
139
140 fn process(f: S) {
141 m!(match f {
142 S { f<|>: 92 } => (),
143 })
144 }
145 ",
146 );
147 assert_debug_snapshot!(completions, @r###"
148 [
149 CompletionItem {
150 label: "foo",
151 source_range: [171; 172),
152 delete: [171; 172),
153 insert: "foo",
154 kind: Field,
155 detail: "u32",
156 },
157 ]
158 "###);
159 }
160
161 #[test]
162 fn only_missing_fields_are_completed_in_destruct_pats() {
163 let completions = complete(
164 r"
165 struct S {
166 foo1: u32,
167 foo2: u32,
168 bar: u32,
169 baz: u32,
170 }
171
172 fn main() {
173 let s = S {
174 foo1: 1,
175 foo2: 2,
176 bar: 3,
177 baz: 4,
178 };
179 if let S { foo1, foo2: a, <|> } = s {}
180 }
181 ",
182 );
183 assert_debug_snapshot!(completions, @r###"
184 [
185 CompletionItem {
186 label: "bar",
187 source_range: [372; 372),
188 delete: [372; 372),
189 insert: "bar",
190 kind: Field,
191 detail: "u32",
192 },
193 CompletionItem {
194 label: "baz",
195 source_range: [372; 372),
196 delete: [372; 372),
197 insert: "baz",
198 kind: Field,
199 detail: "u32",
200 },
201 ]
202 "###);
203 }
204 }
205
206 mod record_pat_tests {
207 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
208 use insta::assert_debug_snapshot;
209
210 fn complete(code: &str) -> Vec<CompletionItem> {
211 do_completion(code, CompletionKind::Reference)
212 }
213
214 #[test]
215 fn test_record_literal_deprecated_field() {
216 let completions = complete(
217 r"
218 struct A {
219 #[deprecated]
220 the_field: u32,
221 }
222 fn foo() {
223 A { the<|> }
224 }
225 ",
226 );
227 assert_debug_snapshot!(completions, @r###"
228 [
229 CompletionItem {
230 label: "the_field",
231 source_range: [142; 145),
232 delete: [142; 145),
233 insert: "the_field",
234 kind: Field,
235 detail: "u32",
236 deprecated: true,
237 },
238 ]
239 "###);
240 }
241
242 #[test]
243 fn test_record_literal_field() {
244 let completions = complete(
245 r"
246 struct A { the_field: u32 }
247 fn foo() {
248 A { the<|> }
249 }
250 ",
251 );
252 assert_debug_snapshot!(completions, @r###"
253 [
254 CompletionItem {
255 label: "the_field",
256 source_range: [83; 86),
257 delete: [83; 86),
258 insert: "the_field",
259 kind: Field,
260 detail: "u32",
261 },
262 ]
263 "###);
264 }
265
266 #[test]
267 fn test_record_literal_enum_variant() {
268 let completions = complete(
269 r"
270 enum E {
271 A { a: u32 }
272 }
273 fn foo() {
274 let _ = E::A { <|> }
275 }
276 ",
277 );
278 assert_debug_snapshot!(completions, @r###"
279 [
280 CompletionItem {
281 label: "a",
282 source_range: [119; 119),
283 delete: [119; 119),
284 insert: "a",
285 kind: Field,
286 detail: "u32",
287 },
288 ]
289 "###);
290 }
291
292 #[test]
293 fn test_record_literal_two_structs() {
294 let completions = complete(
295 r"
296 struct A { a: u32 }
297 struct B { b: u32 }
298
299 fn foo() {
300 let _: A = B { <|> }
301 }
302 ",
303 );
304 assert_debug_snapshot!(completions, @r###"
305 [
306 CompletionItem {
307 label: "b",
308 source_range: [119; 119),
309 delete: [119; 119),
310 insert: "b",
311 kind: Field,
312 detail: "u32",
313 },
314 ]
315 "###);
316 }
317
318 #[test]
319 fn test_record_literal_generic_struct() {
320 let completions = complete(
321 r"
322 struct A<T> { a: T }
323
324 fn foo() {
325 let _: A<u32> = A { <|> }
326 }
327 ",
328 );
329 assert_debug_snapshot!(completions, @r###"
330 [
331 CompletionItem {
332 label: "a",
333 source_range: [93; 93),
334 delete: [93; 93),
335 insert: "a",
336 kind: Field,
337 detail: "u32",
338 },
339 ]
340 "###);
341 }
342
343 #[test]
344 fn test_record_literal_field_in_simple_macro() {
345 let completions = complete(
346 r"
347 macro_rules! m { ($e:expr) => { $e } }
348 struct A { the_field: u32 }
349 fn foo() {
350 m!(A { the<|> })
351 }
352 ",
353 );
354 assert_debug_snapshot!(completions, @r###"
355 [
356 CompletionItem {
357 label: "the_field",
358 source_range: [137; 140),
359 delete: [137; 140),
360 insert: "the_field",
361 kind: Field,
362 detail: "u32",
363 },
364 ]
365 "###);
366 }
367
368 #[test]
369 fn only_missing_fields_are_completed() {
370 let completions = complete(
371 r"
372 struct S {
373 foo1: u32,
374 foo2: u32,
375 bar: u32,
376 baz: u32,
377 }
378
379 fn main() {
380 let foo1 = 1;
381 let s = S {
382 foo1,
383 foo2: 5,
384 <|>
385 }
386 }
387 ",
388 );
389 assert_debug_snapshot!(completions, @r###"
390 [
391 CompletionItem {
392 label: "bar",
393 source_range: [302; 302),
394 delete: [302; 302),
395 insert: "bar",
396 kind: Field,
397 detail: "u32",
398 },
399 CompletionItem {
400 label: "baz",
401 source_range: [302; 302),
402 delete: [302; 302),
403 insert: "baz",
404 kind: Field,
405 detail: "u32",
406 },
407 ]
408 "###);
409 }
410 }
411}
diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs
deleted file mode 100644
index e4e764f58..000000000
--- a/crates/ra_ide/src/completion/complete_record_literal.rs
+++ /dev/null
@@ -1,241 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4use ra_syntax::SmolStr;
5
6/// Complete fields in fields literals.
7pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) {
8 let (ty, variant) = match ctx.record_lit_syntax.as_ref().and_then(|it| {
9 Some((ctx.sema.type_of_expr(&it.clone().into())?, ctx.sema.resolve_record_literal(it)?))
10 }) {
11 Some(it) => it,
12 _ => return,
13 };
14
15 let already_present_names: Vec<SmolStr> = ctx
16 .record_lit_syntax
17 .as_ref()
18 .and_then(|record_literal| record_literal.record_field_list())
19 .map(|field_list| field_list.fields())
20 .map(|fields| {
21 fields
22 .into_iter()
23 .filter_map(|field| field.name_ref())
24 .map(|name_ref| name_ref.text().clone())
25 .collect()
26 })
27 .unwrap_or_default();
28
29 for (field, field_ty) in ty.variant_fields(ctx.db, variant) {
30 if !already_present_names.contains(&SmolStr::from(field.name(ctx.db).to_string())) {
31 acc.add_field(ctx, field, &field_ty);
32 }
33 }
34}
35
36#[cfg(test)]
37mod tests {
38 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
39 use insta::assert_debug_snapshot;
40
41 fn complete(code: &str) -> Vec<CompletionItem> {
42 do_completion(code, CompletionKind::Reference)
43 }
44
45 #[test]
46 fn test_record_literal_deprecated_field() {
47 let completions = complete(
48 r"
49 struct A {
50 #[deprecated]
51 the_field: u32,
52 }
53 fn foo() {
54 A { the<|> }
55 }
56 ",
57 );
58 assert_debug_snapshot!(completions, @r###"
59 [
60 CompletionItem {
61 label: "the_field",
62 source_range: [142; 145),
63 delete: [142; 145),
64 insert: "the_field",
65 kind: Field,
66 detail: "u32",
67 deprecated: true,
68 },
69 ]
70 "###);
71 }
72
73 #[test]
74 fn test_record_literal_field() {
75 let completions = complete(
76 r"
77 struct A { the_field: u32 }
78 fn foo() {
79 A { the<|> }
80 }
81 ",
82 );
83 assert_debug_snapshot!(completions, @r###"
84 [
85 CompletionItem {
86 label: "the_field",
87 source_range: [83; 86),
88 delete: [83; 86),
89 insert: "the_field",
90 kind: Field,
91 detail: "u32",
92 },
93 ]
94 "###);
95 }
96
97 #[test]
98 fn test_record_literal_enum_variant() {
99 let completions = complete(
100 r"
101 enum E {
102 A { a: u32 }
103 }
104 fn foo() {
105 let _ = E::A { <|> }
106 }
107 ",
108 );
109 assert_debug_snapshot!(completions, @r###"
110 [
111 CompletionItem {
112 label: "a",
113 source_range: [119; 119),
114 delete: [119; 119),
115 insert: "a",
116 kind: Field,
117 detail: "u32",
118 },
119 ]
120 "###);
121 }
122
123 #[test]
124 fn test_record_literal_two_structs() {
125 let completions = complete(
126 r"
127 struct A { a: u32 }
128 struct B { b: u32 }
129
130 fn foo() {
131 let _: A = B { <|> }
132 }
133 ",
134 );
135 assert_debug_snapshot!(completions, @r###"
136 [
137 CompletionItem {
138 label: "b",
139 source_range: [119; 119),
140 delete: [119; 119),
141 insert: "b",
142 kind: Field,
143 detail: "u32",
144 },
145 ]
146 "###);
147 }
148
149 #[test]
150 fn test_record_literal_generic_struct() {
151 let completions = complete(
152 r"
153 struct A<T> { a: T }
154
155 fn foo() {
156 let _: A<u32> = A { <|> }
157 }
158 ",
159 );
160 assert_debug_snapshot!(completions, @r###"
161 [
162 CompletionItem {
163 label: "a",
164 source_range: [93; 93),
165 delete: [93; 93),
166 insert: "a",
167 kind: Field,
168 detail: "u32",
169 },
170 ]
171 "###);
172 }
173
174 #[test]
175 fn test_record_literal_field_in_simple_macro() {
176 let completions = complete(
177 r"
178 macro_rules! m { ($e:expr) => { $e } }
179 struct A { the_field: u32 }
180 fn foo() {
181 m!(A { the<|> })
182 }
183 ",
184 );
185 assert_debug_snapshot!(completions, @r###"
186 [
187 CompletionItem {
188 label: "the_field",
189 source_range: [137; 140),
190 delete: [137; 140),
191 insert: "the_field",
192 kind: Field,
193 detail: "u32",
194 },
195 ]
196 "###);
197 }
198
199 #[test]
200 fn only_missing_fields_are_completed() {
201 let completions = complete(
202 r"
203 struct S {
204 foo1: u32,
205 foo2: u32,
206 bar: u32,
207 baz: u32,
208 }
209
210 fn main() {
211 let foo1 = 1;
212 let s = S {
213 foo1,
214 foo2: 5,
215 <|>
216 }
217 }
218 ",
219 );
220 assert_debug_snapshot!(completions, @r###"
221 [
222 CompletionItem {
223 label: "bar",
224 source_range: [302; 302),
225 delete: [302; 302),
226 insert: "bar",
227 kind: Field,
228 detail: "u32",
229 },
230 CompletionItem {
231 label: "baz",
232 source_range: [302; 302),
233 delete: [302; 302),
234 insert: "baz",
235 kind: Field,
236 detail: "u32",
237 },
238 ]
239 "###);
240 }
241}
diff --git a/crates/ra_ide/src/completion/complete_record_pattern.rs b/crates/ra_ide/src/completion/complete_record_pattern.rs
deleted file mode 100644
index 962376428..000000000
--- a/crates/ra_ide/src/completion/complete_record_pattern.rs
+++ /dev/null
@@ -1,118 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4
5pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionContext) {
6 let (ty, variant) = match ctx.record_lit_pat.as_ref().and_then(|it| {
7 Some((ctx.sema.type_of_pat(&it.clone().into())?, ctx.sema.resolve_record_pattern(it)?))
8 }) {
9 Some(it) => it,
10 _ => return,
11 };
12
13 for (field, field_ty) in ty.variant_fields(ctx.db, variant) {
14 acc.add_field(ctx, field, &field_ty);
15 }
16}
17
18#[cfg(test)]
19mod tests {
20 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
21 use insta::assert_debug_snapshot;
22
23 fn complete(code: &str) -> Vec<CompletionItem> {
24 do_completion(code, CompletionKind::Reference)
25 }
26
27 #[test]
28 fn test_record_pattern_field() {
29 let completions = complete(
30 r"
31 struct S { foo: u32 }
32
33 fn process(f: S) {
34 match f {
35 S { f<|>: 92 } => (),
36 }
37 }
38 ",
39 );
40 assert_debug_snapshot!(completions, @r###"
41 [
42 CompletionItem {
43 label: "foo",
44 source_range: [117; 118),
45 delete: [117; 118),
46 insert: "foo",
47 kind: Field,
48 detail: "u32",
49 },
50 ]
51 "###);
52 }
53
54 #[test]
55 fn test_record_pattern_enum_variant() {
56 let completions = complete(
57 r"
58 enum E {
59 S { foo: u32, bar: () }
60 }
61
62 fn process(e: E) {
63 match e {
64 E::S { <|> } => (),
65 }
66 }
67 ",
68 );
69 assert_debug_snapshot!(completions, @r###"
70 [
71 CompletionItem {
72 label: "bar",
73 source_range: [161; 161),
74 delete: [161; 161),
75 insert: "bar",
76 kind: Field,
77 detail: "()",
78 },
79 CompletionItem {
80 label: "foo",
81 source_range: [161; 161),
82 delete: [161; 161),
83 insert: "foo",
84 kind: Field,
85 detail: "u32",
86 },
87 ]
88 "###);
89 }
90
91 #[test]
92 fn test_record_pattern_field_in_simple_macro() {
93 let completions = complete(
94 r"
95 macro_rules! m { ($e:expr) => { $e } }
96 struct S { foo: u32 }
97
98 fn process(f: S) {
99 m!(match f {
100 S { f<|>: 92 } => (),
101 })
102 }
103 ",
104 );
105 assert_debug_snapshot!(completions, @r###"
106 [
107 CompletionItem {
108 label: "foo",
109 source_range: [171; 172),
110 delete: [171; 172),
111 insert: "foo",
112 kind: Field,
113 detail: "u32",
114 },
115 ]
116 "###);
117 }
118}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 319e33b61..fdc0da2c5 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11}; 11};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::AtomTextEdit;
13 13
14use crate::{completion::CompletionOptions, FilePosition}; 14use crate::{completion::CompletionConfig, FilePosition};
15 15
16/// `CompletionContext` is created early during completion to figure out, where 16/// `CompletionContext` is created early during completion to figure out, where
17/// exactly is the cursor, syntax-wise. 17/// exactly is the cursor, syntax-wise.
@@ -19,7 +19,7 @@ use crate::{completion::CompletionOptions, FilePosition};
19pub(crate) struct CompletionContext<'a> { 19pub(crate) struct CompletionContext<'a> {
20 pub(super) sema: Semantics<'a, RootDatabase>, 20 pub(super) sema: Semantics<'a, RootDatabase>,
21 pub(super) db: &'a RootDatabase, 21 pub(super) db: &'a RootDatabase,
22 pub(super) options: &'a CompletionOptions, 22 pub(super) config: &'a CompletionConfig,
23 pub(super) offset: TextUnit, 23 pub(super) offset: TextUnit,
24 /// The token before the cursor, in the original file. 24 /// The token before the cursor, in the original file.
25 pub(super) original_token: SyntaxToken, 25 pub(super) original_token: SyntaxToken,
@@ -61,7 +61,7 @@ impl<'a> CompletionContext<'a> {
61 pub(super) fn new( 61 pub(super) fn new(
62 db: &'a RootDatabase, 62 db: &'a RootDatabase,
63 position: FilePosition, 63 position: FilePosition,
64 options: &'a CompletionOptions, 64 config: &'a CompletionConfig,
65 ) -> Option<CompletionContext<'a>> { 65 ) -> Option<CompletionContext<'a>> {
66 let sema = Semantics::new(db); 66 let sema = Semantics::new(db);
67 67
@@ -85,7 +85,7 @@ impl<'a> CompletionContext<'a> {
85 let mut ctx = CompletionContext { 85 let mut ctx = CompletionContext {
86 sema, 86 sema,
87 db, 87 db,
88 options, 88 config,
89 original_token, 89 original_token,
90 token, 90 token,
91 offset: position.offset, 91 offset: position.offset,
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 60f1b83f3..1c7c0924d 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -106,7 +106,7 @@ impl Completions {
106 }; 106 };
107 107
108 // Add `<>` for generic types 108 // Add `<>` for generic types
109 if ctx.is_path_type && !ctx.has_type_args && ctx.options.add_call_parenthesis { 109 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
110 let has_non_default_type_params = match resolution { 110 let has_non_default_type_params = match resolution {
111 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db), 111 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
112 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db), 112 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
@@ -211,14 +211,14 @@ impl Completions {
211 .detail(function_signature.to_string()); 211 .detail(function_signature.to_string());
212 212
213 // If not an import, add parenthesis automatically. 213 // If not an import, add parenthesis automatically.
214 if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.options.add_call_parenthesis { 214 if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.config.add_call_parenthesis {
215 tested_by!(inserts_parens_for_function_calls); 215 tested_by!(inserts_parens_for_function_calls);
216 216
217 let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 { 217 let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 {
218 (format!("{}()$0", name), format!("{}()", name)) 218 (format!("{}()$0", name), format!("{}()", name))
219 } else { 219 } else {
220 builder = builder.trigger_call_info(); 220 builder = builder.trigger_call_info();
221 let snippet = if ctx.options.add_call_argument_snippets { 221 let snippet = if ctx.config.add_call_argument_snippets {
222 let to_skip = if has_self_param { 1 } else { 0 }; 222 let to_skip = if has_self_param { 1 } else { 0 };
223 let function_params_snippet = function_signature 223 let function_params_snippet = function_signature
224 .parameter_names 224 .parameter_names
@@ -311,7 +311,7 @@ mod tests {
311 311
312 use crate::completion::{ 312 use crate::completion::{
313 test_utils::{do_completion, do_completion_with_options}, 313 test_utils::{do_completion, do_completion_with_options},
314 CompletionItem, CompletionKind, CompletionOptions, 314 CompletionConfig, CompletionItem, CompletionKind,
315 }; 315 };
316 316
317 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> { 317 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> {
@@ -320,7 +320,7 @@ mod tests {
320 320
321 fn do_reference_completion_with_options( 321 fn do_reference_completion_with_options(
322 ra_fixture: &str, 322 ra_fixture: &str,
323 options: CompletionOptions, 323 options: CompletionConfig,
324 ) -> Vec<CompletionItem> { 324 ) -> Vec<CompletionItem> {
325 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options) 325 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options)
326 } 326 }
@@ -589,7 +589,7 @@ mod tests {
589 s.f<|> 589 s.f<|>
590 } 590 }
591 ", 591 ",
592 CompletionOptions { 592 CompletionConfig {
593 add_call_argument_snippets: false, 593 add_call_argument_snippets: false,
594 .. Default::default() 594 .. Default::default()
595 } 595 }
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index 136857315..eb90b5279 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -1,19 +1,19 @@
1//! Runs completion for testing purposes. 1//! Runs completion for testing purposes.
2 2
3use crate::{ 3use crate::{
4 completion::{completion_item::CompletionKind, CompletionOptions}, 4 completion::{completion_item::CompletionKind, CompletionConfig},
5 mock_analysis::{analysis_and_position, single_file_with_position}, 5 mock_analysis::{analysis_and_position, single_file_with_position},
6 CompletionItem, 6 CompletionItem,
7}; 7};
8 8
9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
10 do_completion_with_options(code, kind, &CompletionOptions::default()) 10 do_completion_with_options(code, kind, &CompletionConfig::default())
11} 11}
12 12
13pub(crate) fn do_completion_with_options( 13pub(crate) fn do_completion_with_options(
14 code: &str, 14 code: &str,
15 kind: CompletionKind, 15 kind: CompletionKind,
16 options: &CompletionOptions, 16 options: &CompletionConfig,
17) -> Vec<CompletionItem> { 17) -> Vec<CompletionItem> {
18 let (analysis, position) = if code.contains("//-") { 18 let (analysis, position) = if code.contains("//-") {
19 analysis_and_position(code) 19 analysis_and_position(code)
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index f4f0751c0..4b133b19b 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -11,14 +11,14 @@ use ra_syntax::{
11use crate::{FileId, FunctionSignature}; 11use crate::{FileId, FunctionSignature};
12 12
13#[derive(Clone, Debug, PartialEq, Eq)] 13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct InlayHintsOptions { 14pub struct InlayHintsConfig {
15 pub type_hints: bool, 15 pub type_hints: bool,
16 pub parameter_hints: bool, 16 pub parameter_hints: bool,
17 pub chaining_hints: bool, 17 pub chaining_hints: bool,
18 pub max_length: Option<usize>, 18 pub max_length: Option<usize>,
19} 19}
20 20
21impl Default for InlayHintsOptions { 21impl Default for InlayHintsConfig {
22 fn default() -> Self { 22 fn default() -> Self {
23 Self { type_hints: true, parameter_hints: true, chaining_hints: true, max_length: None } 23 Self { type_hints: true, parameter_hints: true, chaining_hints: true, max_length: None }
24 } 24 }
@@ -41,7 +41,7 @@ pub struct InlayHint {
41pub(crate) fn inlay_hints( 41pub(crate) fn inlay_hints(
42 db: &RootDatabase, 42 db: &RootDatabase,
43 file_id: FileId, 43 file_id: FileId,
44 options: &InlayHintsOptions, 44 config: &InlayHintsConfig,
45) -> Vec<InlayHint> { 45) -> Vec<InlayHint> {
46 let _p = profile("inlay_hints"); 46 let _p = profile("inlay_hints");
47 let sema = Semantics::new(db); 47 let sema = Semantics::new(db);
@@ -50,14 +50,14 @@ pub(crate) fn inlay_hints(
50 let mut res = Vec::new(); 50 let mut res = Vec::new();
51 for node in file.syntax().descendants() { 51 for node in file.syntax().descendants() {
52 if let Some(expr) = ast::Expr::cast(node.clone()) { 52 if let Some(expr) = ast::Expr::cast(node.clone()) {
53 get_chaining_hints(&mut res, &sema, options, expr); 53 get_chaining_hints(&mut res, &sema, config, expr);
54 } 54 }
55 55
56 match_ast! { 56 match_ast! {
57 match node { 57 match node {
58 ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, 58 ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
59 ast::MethodCallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, 59 ast::MethodCallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
60 ast::BindPat(it) => { get_bind_pat_hints(&mut res, &sema, options, it); }, 60 ast::BindPat(it) => { get_bind_pat_hints(&mut res, &sema, config, it); },
61 _ => (), 61 _ => (),
62 } 62 }
63 } 63 }
@@ -68,15 +68,14 @@ pub(crate) fn inlay_hints(
68fn get_chaining_hints( 68fn get_chaining_hints(
69 acc: &mut Vec<InlayHint>, 69 acc: &mut Vec<InlayHint>,
70 sema: &Semantics<RootDatabase>, 70 sema: &Semantics<RootDatabase>,
71 options: &InlayHintsOptions, 71 config: &InlayHintsConfig,
72 expr: ast::Expr, 72 expr: ast::Expr,
73) -> Option<()> { 73) -> Option<()> {
74 if !options.chaining_hints { 74 if !config.chaining_hints {
75 return None; 75 return None;
76 } 76 }
77 77
78 let ty = sema.type_of_expr(&expr)?; 78 if matches!(expr, ast::Expr::RecordLit(_)) {
79 if ty.is_unknown() {
80 return None; 79 return None;
81 } 80 }
82 81
@@ -95,7 +94,18 @@ fn get_chaining_hints(
95 let next = tokens.next()?.kind(); 94 let next = tokens.next()?.kind();
96 let next_next = tokens.next()?.kind(); 95 let next_next = tokens.next()?.kind();
97 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { 96 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT {
98 let label = ty.display_truncated(sema.db, options.max_length).to_string(); 97 let ty = sema.type_of_expr(&expr)?;
98 if ty.is_unknown() {
99 return None;
100 }
101 if matches!(expr, ast::Expr::PathExpr(_)) {
102 if let Some(Adt::Struct(st)) = ty.as_adt() {
103 if st.fields(sema.db).is_empty() {
104 return None;
105 }
106 }
107 }
108 let label = ty.display_truncated(sema.db, config.max_length).to_string();
99 acc.push(InlayHint { 109 acc.push(InlayHint {
100 range: expr.syntax().text_range(), 110 range: expr.syntax().text_range(),
101 kind: InlayKind::ChainingHint, 111 kind: InlayKind::ChainingHint,
@@ -108,10 +118,10 @@ fn get_chaining_hints(
108fn get_param_name_hints( 118fn get_param_name_hints(
109 acc: &mut Vec<InlayHint>, 119 acc: &mut Vec<InlayHint>,
110 sema: &Semantics<RootDatabase>, 120 sema: &Semantics<RootDatabase>,
111 options: &InlayHintsOptions, 121 config: &InlayHintsConfig,
112 expr: ast::Expr, 122 expr: ast::Expr,
113) -> Option<()> { 123) -> Option<()> {
114 if !options.parameter_hints { 124 if !config.parameter_hints {
115 return None; 125 return None;
116 } 126 }
117 127
@@ -148,10 +158,10 @@ fn get_param_name_hints(
148fn get_bind_pat_hints( 158fn get_bind_pat_hints(
149 acc: &mut Vec<InlayHint>, 159 acc: &mut Vec<InlayHint>,
150 sema: &Semantics<RootDatabase>, 160 sema: &Semantics<RootDatabase>,
151 options: &InlayHintsOptions, 161 config: &InlayHintsConfig,
152 pat: ast::BindPat, 162 pat: ast::BindPat,
153) -> Option<()> { 163) -> Option<()> {
154 if !options.type_hints { 164 if !config.type_hints {
155 return None; 165 return None;
156 } 166 }
157 167
@@ -164,7 +174,7 @@ fn get_bind_pat_hints(
164 acc.push(InlayHint { 174 acc.push(InlayHint {
165 range: pat.syntax().text_range(), 175 range: pat.syntax().text_range(),
166 kind: InlayKind::TypeHint, 176 kind: InlayKind::TypeHint,
167 label: ty.display_truncated(sema.db, options.max_length).to_string().into(), 177 label: ty.display_truncated(sema.db, config.max_length).to_string().into(),
168 }); 178 });
169 Some(()) 179 Some(())
170} 180}
@@ -270,7 +280,7 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
270 280
271#[cfg(test)] 281#[cfg(test)]
272mod tests { 282mod tests {
273 use crate::inlay_hints::InlayHintsOptions; 283 use crate::inlay_hints::InlayHintsConfig;
274 use insta::assert_debug_snapshot; 284 use insta::assert_debug_snapshot;
275 285
276 use crate::mock_analysis::single_file; 286 use crate::mock_analysis::single_file;
@@ -284,7 +294,7 @@ mod tests {
284 let _x = foo(4, 4); 294 let _x = foo(4, 4);
285 }"#, 295 }"#,
286 ); 296 );
287 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 297 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
288 [ 298 [
289 InlayHint { 299 InlayHint {
290 range: [106; 107), 300 range: [106; 107),
@@ -308,7 +318,7 @@ mod tests {
308 let _x = foo(4, 4); 318 let _x = foo(4, 4);
309 }"#, 319 }"#,
310 ); 320 );
311 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###); 321 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
312 } 322 }
313 323
314 #[test] 324 #[test]
@@ -320,7 +330,7 @@ mod tests {
320 let _x = foo(4, 4); 330 let _x = foo(4, 4);
321 }"#, 331 }"#,
322 ); 332 );
323 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 333 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
324 [ 334 [
325 InlayHint { 335 InlayHint {
326 range: [97; 99), 336 range: [97; 99),
@@ -344,7 +354,7 @@ fn main() {
344}"#, 354}"#,
345 ); 355 );
346 356
347 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 357 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
348 [ 358 [
349 InlayHint { 359 InlayHint {
350 range: [69; 71), 360 range: [69; 71),
@@ -401,7 +411,7 @@ fn main() {
401}"#, 411}"#,
402 ); 412 );
403 413
404 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 414 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
405 [ 415 [
406 InlayHint { 416 InlayHint {
407 range: [193; 197), 417 range: [193; 197),
@@ -481,7 +491,7 @@ fn main() {
481}"#, 491}"#,
482 ); 492 );
483 493
484 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 494 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
485 [ 495 [
486 InlayHint { 496 InlayHint {
487 range: [21; 30), 497 range: [21; 30),
@@ -545,7 +555,7 @@ fn main() {
545}"#, 555}"#,
546 ); 556 );
547 557
548 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 558 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
549 [ 559 [
550 InlayHint { 560 InlayHint {
551 range: [21; 30), 561 range: [21; 30),
@@ -595,7 +605,7 @@ fn main() {
595}"#, 605}"#,
596 ); 606 );
597 607
598 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 608 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
599 [ 609 [
600 InlayHint { 610 InlayHint {
601 range: [188; 192), 611 range: [188; 192),
@@ -690,7 +700,7 @@ fn main() {
690}"#, 700}"#,
691 ); 701 );
692 702
693 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 703 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
694 [ 704 [
695 InlayHint { 705 InlayHint {
696 range: [188; 192), 706 range: [188; 192),
@@ -785,7 +795,7 @@ fn main() {
785}"#, 795}"#,
786 ); 796 );
787 797
788 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 798 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
789 [ 799 [
790 InlayHint { 800 InlayHint {
791 range: [252; 256), 801 range: [252; 256),
@@ -857,7 +867,7 @@ fn main() {
857}"#, 867}"#,
858 ); 868 );
859 869
860 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###" 870 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
861 [ 871 [
862 InlayHint { 872 InlayHint {
863 range: [74; 75), 873 range: [74; 75),
@@ -945,7 +955,7 @@ fn main() {
945}"#, 955}"#,
946 ); 956 );
947 957
948 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 958 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
949 [ 959 [
950 InlayHint { 960 InlayHint {
951 range: [798; 809), 961 range: [798; 809),
@@ -1067,7 +1077,7 @@ fn main() {
1067}"#, 1077}"#,
1068 ); 1078 );
1069 1079
1070 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###" 1080 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1071 [] 1081 []
1072 "### 1082 "###
1073 ); 1083 );
@@ -1093,7 +1103,7 @@ fn main() {
1093}"#, 1103}"#,
1094 ); 1104 );
1095 1105
1096 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###" 1106 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1097 [] 1107 []
1098 "### 1108 "###
1099 ); 1109 );
@@ -1115,7 +1125,7 @@ fn main() {
1115 .into_c(); 1125 .into_c();
1116 }"#, 1126 }"#,
1117 ); 1127 );
1118 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 1128 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1119 [ 1129 [
1120 InlayHint { 1130 InlayHint {
1121 range: [232; 269), 1131 range: [232; 269),
@@ -1144,7 +1154,7 @@ fn main() {
1144 let c = A(B(C)).into_b().into_c(); 1154 let c = A(B(C)).into_b().into_c();
1145 }"#, 1155 }"#,
1146 ); 1156 );
1147 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###); 1157 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
1148 } 1158 }
1149 1159
1150 #[test] 1160 #[test]
@@ -1154,32 +1164,35 @@ fn main() {
1154 struct A { pub b: B } 1164 struct A { pub b: B }
1155 struct B { pub c: C } 1165 struct B { pub c: C }
1156 struct C(pub bool); 1166 struct C(pub bool);
1167 struct D;
1168
1169 impl D {
1170 fn foo(&self) -> i32 { 42 }
1171 }
1157 1172
1158 fn main() { 1173 fn main() {
1159 let x = A { b: B { c: C(true) } } 1174 let x = A { b: B { c: C(true) } }
1160 .b 1175 .b
1161 .c 1176 .c
1162 .0; 1177 .0;
1178 let x = D
1179 .foo();
1163 }"#, 1180 }"#,
1164 ); 1181 );
1165 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 1182 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1166 [ 1183 [
1167 InlayHint { 1184 InlayHint {
1168 range: [150; 221), 1185 range: [252; 323),
1169 kind: ChainingHint, 1186 kind: ChainingHint,
1170 label: "C", 1187 label: "C",
1171 }, 1188 },
1172 InlayHint { 1189 InlayHint {
1173 range: [150; 198), 1190 range: [252; 300),
1174 kind: ChainingHint, 1191 kind: ChainingHint,
1175 label: "B", 1192 label: "B",
1176 }, 1193 },
1177 InlayHint { 1194 ]
1178 range: [150; 175), 1195 "###);
1179 kind: ChainingHint,
1180 label: "A",
1181 },
1182 ]"###);
1183 } 1196 }
1184 1197
1185 #[test] 1198 #[test]
@@ -1204,7 +1217,7 @@ fn main() {
1204 .into_c(); 1217 .into_c();
1205 }"#, 1218 }"#,
1206 ); 1219 );
1207 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 1220 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1208 [ 1221 [
1209 InlayHint { 1222 InlayHint {
1210 range: [403; 452), 1223 range: [403; 452),
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index e43414985..285381086 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -62,13 +62,13 @@ use crate::display::ToNav;
62pub use crate::{ 62pub use crate::{
63 assists::{Assist, AssistId}, 63 assists::{Assist, AssistId},
64 call_hierarchy::CallItem, 64 call_hierarchy::CallItem,
65 completion::{CompletionItem, CompletionItemKind, CompletionOptions, InsertTextFormat}, 65 completion::{CompletionConfig, CompletionItem, CompletionItemKind, InsertTextFormat},
66 diagnostics::Severity, 66 diagnostics::Severity,
67 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 67 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
68 expand_macro::ExpandedMacro, 68 expand_macro::ExpandedMacro,
69 folding_ranges::{Fold, FoldKind}, 69 folding_ranges::{Fold, FoldKind},
70 hover::HoverResult, 70 hover::HoverResult,
71 inlay_hints::{InlayHint, InlayHintsOptions, InlayKind}, 71 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
72 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, 72 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
73 runnables::{Runnable, RunnableKind, TestId}, 73 runnables::{Runnable, RunnableKind, TestId},
74 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 74 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
@@ -138,6 +138,11 @@ impl AnalysisHost {
138 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost { 138 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost {
139 AnalysisHost { db: RootDatabase::new(lru_capacity) } 139 AnalysisHost { db: RootDatabase::new(lru_capacity) }
140 } 140 }
141
142 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
143 self.db.update_lru_capacity(lru_capacity);
144 }
145
141 /// Returns a snapshot of the current state, which you can query for 146 /// Returns a snapshot of the current state, which you can query for
142 /// semantic information. 147 /// semantic information.
143 pub fn analysis(&self) -> Analysis { 148 pub fn analysis(&self) -> Analysis {
@@ -320,9 +325,9 @@ impl Analysis {
320 pub fn inlay_hints( 325 pub fn inlay_hints(
321 &self, 326 &self,
322 file_id: FileId, 327 file_id: FileId,
323 inlay_hint_opts: &InlayHintsOptions, 328 config: &InlayHintsConfig,
324 ) -> Cancelable<Vec<InlayHint>> { 329 ) -> Cancelable<Vec<InlayHint>> {
325 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, inlay_hint_opts)) 330 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, config))
326 } 331 }
327 332
328 /// Returns the set of folding ranges. 333 /// Returns the set of folding ranges.
@@ -445,9 +450,9 @@ impl Analysis {
445 pub fn completions( 450 pub fn completions(
446 &self, 451 &self,
447 position: FilePosition, 452 position: FilePosition,
448 options: &CompletionOptions, 453 config: &CompletionConfig,
449 ) -> Cancelable<Option<Vec<CompletionItem>>> { 454 ) -> Cancelable<Option<Vec<CompletionItem>>> {
450 self.with_db(|db| completion::completions(db, position, options).map(Into::into)) 455 self.with_db(|db| completion::completions(db, position, config).map(Into::into))
451 } 456 }
452 457
453 /// Computes assists (aka code actions aka intentions) for the given 458 /// Computes assists (aka code actions aka intentions) for the given
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 1c9710a5d..1abb891c1 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -5,7 +5,7 @@ use ra_db::{SourceDatabase, SourceDatabaseExt};
5use ra_ide_db::symbol_index::SymbolsDatabase; 5use ra_ide_db::symbol_index::SymbolsDatabase;
6use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
7use ra_syntax::ast::make::try_expr_from_text; 7use ra_syntax::ast::make::try_expr_from_text;
8use ra_syntax::ast::{AstToken, Comment}; 8use ra_syntax::ast::{AstToken, Comment, RecordField, RecordLit};
9use ra_syntax::{AstNode, SyntaxElement, SyntaxNode}; 9use ra_syntax::{AstNode, SyntaxElement, SyntaxNode};
10use ra_text_edit::{TextEdit, TextEditBuilder}; 10use ra_text_edit::{TextEdit, TextEditBuilder};
11use rustc_hash::FxHashMap; 11use rustc_hash::FxHashMap;
@@ -186,47 +186,102 @@ fn create_name<'a>(name: &str, vars: &'a mut Vec<Var>) -> Result<&'a str, SsrErr
186} 186}
187 187
188fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches { 188fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
189 fn check_record_lit(
190 pattern: RecordLit,
191 code: RecordLit,
192 placeholders: &[Var],
193 match_: Match,
194 ) -> Option<Match> {
195 let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?;
196
197 let mut pattern_fields =
198 pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]);
199 let mut code_fields =
200 code.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]);
201
202 if pattern_fields.len() != code_fields.len() {
203 return None;
204 }
205
206 let by_name = |a: &RecordField, b: &RecordField| {
207 a.name_ref()
208 .map(|x| x.syntax().text().to_string())
209 .cmp(&b.name_ref().map(|x| x.syntax().text().to_string()))
210 };
211 pattern_fields.sort_by(by_name);
212 code_fields.sort_by(by_name);
213
214 pattern_fields.into_iter().zip(code_fields.into_iter()).fold(
215 Some(match_),
216 |accum, (a, b)| {
217 accum.and_then(|match_| check_opt_nodes(Some(a), Some(b), placeholders, match_))
218 },
219 )
220 }
221
222 fn check_opt_nodes(
223 pattern: Option<impl AstNode>,
224 code: Option<impl AstNode>,
225 placeholders: &[Var],
226 match_: Match,
227 ) -> Option<Match> {
228 match (pattern, code) {
229 (Some(pattern), Some(code)) => check(
230 &SyntaxElement::from(pattern.syntax().clone()),
231 &SyntaxElement::from(code.syntax().clone()),
232 placeholders,
233 match_,
234 ),
235 (None, None) => Some(match_),
236 _ => None,
237 }
238 }
239
189 fn check( 240 fn check(
190 pattern: &SyntaxElement, 241 pattern: &SyntaxElement,
191 code: &SyntaxElement, 242 code: &SyntaxElement,
192 placeholders: &[Var], 243 placeholders: &[Var],
193 mut match_: Match, 244 mut match_: Match,
194 ) -> Option<Match> { 245 ) -> Option<Match> {
195 match (pattern, code) { 246 match (&pattern, &code) {
196 (SyntaxElement::Token(ref pattern), SyntaxElement::Token(ref code)) => { 247 (SyntaxElement::Token(pattern), SyntaxElement::Token(code)) => {
197 if pattern.text() == code.text() { 248 if pattern.text() == code.text() {
198 Some(match_) 249 Some(match_)
199 } else { 250 } else {
200 None 251 None
201 } 252 }
202 } 253 }
203 (SyntaxElement::Node(ref pattern), SyntaxElement::Node(ref code)) => { 254 (SyntaxElement::Node(pattern), SyntaxElement::Node(code)) => {
204 if placeholders.iter().any(|n| n.0.as_str() == pattern.text()) { 255 if placeholders.iter().any(|n| n.0.as_str() == pattern.text()) {
205 match_.binding.insert(Var(pattern.text().to_string()), code.clone()); 256 match_.binding.insert(Var(pattern.text().to_string()), code.clone());
206 Some(match_) 257 Some(match_)
207 } else { 258 } else {
208 let mut pattern_children = pattern 259 if let (Some(pattern), Some(code)) =
209 .children_with_tokens() 260 (RecordLit::cast(pattern.clone()), RecordLit::cast(code.clone()))
210 .filter(|element| !element.kind().is_trivia()); 261 {
211 let mut code_children = 262 check_record_lit(pattern, code, placeholders, match_)
212 code.children_with_tokens().filter(|element| !element.kind().is_trivia()); 263 } else {
213 let new_ignored_comments = code.children_with_tokens().filter_map(|element| { 264 let mut pattern_children = pattern
214 element.as_token().and_then(|token| Comment::cast(token.clone())) 265 .children_with_tokens()
215 }); 266 .filter(|element| !element.kind().is_trivia());
216 match_.ignored_comments.extend(new_ignored_comments); 267 let mut code_children = code
217 let match_from_children = pattern_children 268 .children_with_tokens()
218 .by_ref() 269 .filter(|element| !element.kind().is_trivia());
219 .zip(code_children.by_ref()) 270 let new_ignored_comments =
220 .fold(Some(match_), |accum, (a, b)| { 271 code.children_with_tokens().filter_map(|element| {
221 accum.and_then(|match_| check(&a, &b, placeholders, match_)) 272 element.as_token().and_then(|token| Comment::cast(token.clone()))
222 }); 273 });
223 match_from_children.and_then(|match_| { 274 match_.ignored_comments.extend(new_ignored_comments);
224 if pattern_children.count() == 0 && code_children.count() == 0 { 275 pattern_children
225 Some(match_) 276 .by_ref()
226 } else { 277 .zip(code_children.by_ref())
227 None 278 .fold(Some(match_), |accum, (a, b)| {
228 } 279 accum.and_then(|match_| check(&a, &b, placeholders, match_))
229 }) 280 })
281 .filter(|_| {
282 pattern_children.next().is_none() && code_children.next().is_none()
283 })
284 }
230 } 285 }
231 } 286 }
232 _ => None, 287 _ => None,
@@ -434,4 +489,13 @@ mod tests {
434 "fn main() { bar(5)/* using 5 */ }", 489 "fn main() { bar(5)/* using 5 */ }",
435 ) 490 )
436 } 491 }
492
493 #[test]
494 fn ssr_struct_lit() {
495 assert_ssr_transform(
496 "foo{a: $a:expr, b: $b:expr} ==>> foo::new($a, $b)",
497 "fn main() { foo{b:2, a:1} }",
498 "fn main() { foo::new(1, 2) }",
499 )
500 }
437} 501}
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index 4faeefa8d..e6f2d36e9 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -115,12 +115,16 @@ impl RootDatabase {
115 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); 115 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
116 db.set_local_roots_with_durability(Default::default(), Durability::HIGH); 116 db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
117 db.set_library_roots_with_durability(Default::default(), Durability::HIGH); 117 db.set_library_roots_with_durability(Default::default(), Durability::HIGH);
118 let lru_capacity = lru_capacity.unwrap_or(ra_db::DEFAULT_LRU_CAP); 118 db.update_lru_capacity(lru_capacity);
119 db.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_capacity);
120 db.query_mut(hir::db::ParseMacroQuery).set_lru_capacity(lru_capacity);
121 db.query_mut(hir::db::MacroExpandQuery).set_lru_capacity(lru_capacity);
122 db 119 db
123 } 120 }
121
122 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
123 let lru_capacity = lru_capacity.unwrap_or(ra_db::DEFAULT_LRU_CAP);
124 self.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_capacity);