aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock88
-rw-r--r--README.md1
-rw-r--r--crates/paths/Cargo.toml8
-rw-r--r--crates/paths/src/lib.rs123
-rw-r--r--crates/ra_assists/src/assist_context.rs2
-rw-r--r--crates/ra_assists/src/ast_transform.rs2
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs50
-rw-r--r--crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs280
-rw-r--r--crates/ra_assists/src/utils.rs2
-rw-r--r--crates/ra_assists/src/utils/insert_use.rs24
-rw-r--r--crates/ra_hir_def/src/diagnostics.rs3
-rw-r--r--crates/ra_hir_def/src/find_path.rs59
-rw-r--r--crates/ra_hir_def/src/import_map.rs39
-rw-r--r--crates/ra_hir_def/src/nameres.rs3
-rw-r--r--crates/ra_hir_def/src/nameres/mod_resolution.rs8
-rw-r--r--crates/ra_hir_def/src/path.rs2
-rw-r--r--crates/ra_hir_ty/src/_match.rs1045
-rw-r--r--crates/ra_ide/src/completion.rs1
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs940
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs8
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs52
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs26
-rw-r--r--crates/ra_ide/src/completion/patterns.rs194
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs46
-rw-r--r--crates/ra_ide/src/diagnostics.rs35
-rw-r--r--crates/ra_ide/src/lib.rs6
-rw-r--r--crates/ra_ide/src/prime_caches.rs2
-rw-r--r--crates/ra_ide/src/references/rename.rs117
-rw-r--r--crates/ra_ide/src/snapshots/highlight_doctest.html28
-rw-r--r--crates/ra_ide/src/snapshots/highlight_injection.html2
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html8
-rw-r--r--crates/ra_ide/src/snapshots/highlight_unsafe.html2
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html10
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html2
-rw-r--r--crates/ra_ide/src/ssr.rs74
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs122
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs4
-rw-r--r--crates/ra_ide/src/syntax_highlighting/injection.rs16
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs2
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs32
-rw-r--r--crates/ra_ide_db/src/source_change.rs6
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs6
-rw-r--r--crates/ra_project_model/src/lib.rs32
-rw-r--r--crates/ra_syntax/src/algo.rs8
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs274
-rw-r--r--crates/ra_syntax/src/ast/generated/tokens.rs8
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs10
-rw-r--r--crates/ra_syntax/src/lib.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast1
-rw-r--r--crates/rust-analyzer/src/bin/main.rs8
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs32
-rw-r--r--crates/rust-analyzer/src/config.rs11
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs6
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap86
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap86
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs285
-rw-r--r--crates/rust-analyzer/src/from_proto.rs2
-rw-r--r--crates/rust-analyzer/src/global_state.rs56
-rw-r--r--crates/rust-analyzer/src/main_loop.rs45
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs4
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs1
-rw-r--r--crates/rust-analyzer/src/to_proto.rs175
-rw-r--r--crates/test_utils/src/lib.rs12
-rw-r--r--crates/vfs/Cargo.toml14
-rw-r--r--crates/vfs/src/file_set.rs99
-rw-r--r--crates/vfs/src/lib.rs138
-rw-r--r--crates/vfs/src/loader.rs69
-rw-r--r--crates/vfs/src/path_interner.rs31
-rw-r--r--crates/vfs/src/vfs_path.rs49
-rw-r--r--crates/vfs/src/walkdir_loader.rs108
-rw-r--r--docs/dev/README.md86
-rw-r--r--editors/code/package.json18
-rw-r--r--xtask/src/codegen/gen_syntax.rs6
74 files changed, 3381 insertions, 1864 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 308e36836..f6d5b900f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -10,6 +10,12 @@ dependencies = [
10] 10]
11 11
12[[package]] 12[[package]]
13name = "adler32"
14version = "1.1.0"
15source = "registry+https://github.com/rust-lang/crates.io-index"
16checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d"
17
18[[package]]
13name = "aho-corasick" 19name = "aho-corasick"
14version = "0.7.10" 20version = "0.7.10"
15source = "registry+https://github.com/rust-lang/crates.io-index" 21source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -55,22 +61,23 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
55 61
56[[package]] 62[[package]]
57name = "backtrace" 63name = "backtrace"
58version = "0.3.48" 64version = "0.3.49"
59source = "registry+https://github.com/rust-lang/crates.io-index" 65source = "registry+https://github.com/rust-lang/crates.io-index"
60checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" 66checksum = "05100821de9e028f12ae3d189176b41ee198341eb8f369956407fea2f5cc666c"
61dependencies = [ 67dependencies = [
62 "addr2line", 68 "addr2line",
63 "cfg-if", 69 "cfg-if",
64 "libc", 70 "libc",
71 "miniz_oxide",
65 "object", 72 "object",
66 "rustc-demangle", 73 "rustc-demangle",
67] 74]
68 75
69[[package]] 76[[package]]
70name = "base64" 77name = "base64"
71version = "0.12.1" 78version = "0.12.2"
72source = "registry+https://github.com/rust-lang/crates.io-index" 79source = "registry+https://github.com/rust-lang/crates.io-index"
73checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42" 80checksum = "e223af0dc48c96d4f8342ec01a4974f139df863896b316681efd36742f22cc67"
74 81
75[[package]] 82[[package]]
76name = "bitflags" 83name = "bitflags"
@@ -438,9 +445,9 @@ dependencies = [
438 445
439[[package]] 446[[package]]
440name = "hermit-abi" 447name = "hermit-abi"
441version = "0.1.13" 448version = "0.1.14"
442source = "registry+https://github.com/rust-lang/crates.io-index" 449source = "registry+https://github.com/rust-lang/crates.io-index"
443checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" 450checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909"
444dependencies = [ 451dependencies = [
445 "libc", 452 "libc",
446] 453]
@@ -695,6 +702,15 @@ dependencies = [
695] 702]
696 703
697[[package]] 704[[package]]
705name = "miniz_oxide"
706version = "0.3.7"
707source = "registry+https://github.com/rust-lang/crates.io-index"
708checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
709dependencies = [
710 "adler32",
711]
712
713[[package]]
698name = "mio" 714name = "mio"
699version = "0.6.22" 715version = "0.6.22"
700source = "registry+https://github.com/rust-lang/crates.io-index" 716source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -778,9 +794,9 @@ dependencies = [
778 794
779[[package]] 795[[package]]
780name = "object" 796name = "object"
781version = "0.19.0" 797version = "0.20.0"
782source = "registry+https://github.com/rust-lang/crates.io-index" 798source = "registry+https://github.com/rust-lang/crates.io-index"
783checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" 799checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
784 800
785[[package]] 801[[package]]
786name = "once_cell" 802name = "once_cell"
@@ -814,9 +830,9 @@ dependencies = [
814 830
815[[package]] 831[[package]]
816name = "paste" 832name = "paste"
817version = "0.1.16" 833version = "0.1.17"
818source = "registry+https://github.com/rust-lang/crates.io-index" 834source = "registry+https://github.com/rust-lang/crates.io-index"
819checksum = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec" 835checksum = "026c63fe245362be0322bfec5a9656d458d13f9cfb1785d1b38458b9968e8080"
820dependencies = [ 836dependencies = [
821 "paste-impl", 837 "paste-impl",
822 "proc-macro-hack", 838 "proc-macro-hack",
@@ -824,17 +840,18 @@ dependencies = [
824 840
825[[package]] 841[[package]]
826name = "paste-impl" 842name = "paste-impl"
827version = "0.1.16" 843version = "0.1.17"
828source = "registry+https://github.com/rust-lang/crates.io-index" 844source = "registry+https://github.com/rust-lang/crates.io-index"
829checksum = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32" 845checksum = "7b9281a268ec213237dcd2aa3c3d0f46681b04ced37c1616fd36567a9e6954b0"
830dependencies = [ 846dependencies = [
831 "proc-macro-hack", 847 "proc-macro-hack",
832 "proc-macro2",
833 "quote",
834 "syn",
835] 848]
836 849
837[[package]] 850[[package]]
851name = "paths"
852version = "0.1.0"
853
854[[package]]
838name = "percent-encoding" 855name = "percent-encoding"
839version = "2.1.0" 856version = "2.1.0"
840source = "registry+https://github.com/rust-lang/crates.io-index" 857source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -852,9 +869,9 @@ dependencies = [
852 869
853[[package]] 870[[package]]
854name = "pico-args" 871name = "pico-args"
855version = "0.3.1" 872version = "0.3.2"
856source = "registry+https://github.com/rust-lang/crates.io-index" 873source = "registry+https://github.com/rust-lang/crates.io-index"
857checksum = "3ad1f1b834a05d42dae330066e9699a173b28185b3bdc3dbf14ca239585de8cc" 874checksum = "6a71836ceac43f0349e3bd964f5bb902f7b003916f32a4ad00354dafc447fa8f"
858 875
859[[package]] 876[[package]]
860name = "plain" 877name = "plain"
@@ -1271,10 +1288,11 @@ dependencies = [
1271 1288
1272[[package]] 1289[[package]]
1273name = "rayon" 1290name = "rayon"
1274version = "1.3.0" 1291version = "1.3.1"
1275source = "registry+https://github.com/rust-lang/crates.io-index" 1292source = "registry+https://github.com/rust-lang/crates.io-index"
1276checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" 1293checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080"
1277dependencies = [ 1294dependencies = [
1295 "autocfg",
1278 "crossbeam-deque", 1296 "crossbeam-deque",
1279 "either", 1297 "either",
1280 "rayon-core", 1298 "rayon-core",
@@ -1282,9 +1300,9 @@ dependencies = [
1282 1300
1283[[package]] 1301[[package]]
1284name = "rayon-core" 1302name = "rayon-core"
1285version = "1.7.0" 1303version = "1.7.1"
1286source = "registry+https://github.com/rust-lang/crates.io-index" 1304source = "registry+https://github.com/rust-lang/crates.io-index"
1287checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" 1305checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280"
1288dependencies = [ 1306dependencies = [
1289 "crossbeam-deque", 1307 "crossbeam-deque",
1290 "crossbeam-queue", 1308 "crossbeam-queue",
@@ -1319,15 +1337,15 @@ checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
1319 1337
1320[[package]] 1338[[package]]
1321name = "relative-path" 1339name = "relative-path"
1322version = "1.0.0" 1340version = "1.2.1"
1323source = "registry+https://github.com/rust-lang/crates.io-index" 1341source = "registry+https://github.com/rust-lang/crates.io-index"
1324checksum = "bedde000f40f2921ce439ea165c9c53fd629bfa115140c72e22aceacb4a21954" 1342checksum = "c602122c47b382cd045b10866a084b184035d45d8c2609cdd3762852ddfae2a1"
1325 1343
1326[[package]] 1344[[package]]
1327name = "remove_dir_all" 1345name = "remove_dir_all"
1328version = "0.5.2" 1346version = "0.5.3"
1329source = "registry+https://github.com/rust-lang/crates.io-index" 1347source = "registry+https://github.com/rust-lang/crates.io-index"
1330checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" 1348checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
1331dependencies = [ 1349dependencies = [
1332 "winapi 0.3.8", 1350 "winapi 0.3.8",
1333] 1351]
@@ -1501,18 +1519,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
1501 1519
1502[[package]] 1520[[package]]
1503name = "serde" 1521name = "serde"
1504version = "1.0.111" 1522version = "1.0.112"
1505source = "registry+https://github.com/rust-lang/crates.io-index" 1523source = "registry+https://github.com/rust-lang/crates.io-index"
1506checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" 1524checksum = "736aac72d1eafe8e5962d1d1c3d99b0df526015ba40915cb3c49d042e92ec243"
1507dependencies = [ 1525dependencies = [
1508 "serde_derive", 1526 "serde_derive",
1509] 1527]
1510 1528
1511[[package]] 1529[[package]]
1512name = "serde_derive" 1530name = "serde_derive"
1513version = "1.0.111" 1531version = "1.0.112"
1514source = "registry+https://github.com/rust-lang/crates.io-index" 1532source = "registry+https://github.com/rust-lang/crates.io-index"
1515checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" 1533checksum = "bf0343ce212ac0d3d6afd9391ac8e9c9efe06b533c8d33f660f6390cc4093f57"
1516dependencies = [ 1534dependencies = [
1517 "proc-macro2", 1535 "proc-macro2",
1518 "quote", 1536 "quote",
@@ -1726,6 +1744,18 @@ dependencies = [
1726] 1744]
1727 1745
1728[[package]] 1746[[package]]
1747name = "vfs"
1748version = "0.1.0"
1749dependencies = [
1750 "crossbeam-channel",
1751 "globset",
1752 "jod-thread",
1753 "paths",
1754 "rustc-hash",
1755 "walkdir",
1756]
1757
1758[[package]]
1729name = "walkdir" 1759name = "walkdir"
1730version = "2.3.1" 1760version = "2.3.1"
1731source = "registry+https://github.com/rust-lang/crates.io-index" 1761source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/README.md b/README.md
index 1e7c3e9b4..7ba705e73 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,7 @@ Work on rust-analyzer is sponsored by
12 12
13[<img src="https://user-images.githubusercontent.com/1711539/58105231-cf306900-7bee-11e9-83d8-9f1102e59d29.png" alt="Ferrous Systems" width="300">](https://ferrous-systems.com/) 13[<img src="https://user-images.githubusercontent.com/1711539/58105231-cf306900-7bee-11e9-83d8-9f1102e59d29.png" alt="Ferrous Systems" width="300">](https://ferrous-systems.com/)
14- [Mozilla](https://www.mozilla.org/en-US/) 14- [Mozilla](https://www.mozilla.org/en-US/)
15- [Embark Studios](https://embark-studios.com/)
15- [freiheit.com](https://www.freiheit.com) 16- [freiheit.com](https://www.freiheit.com)
16 17
17## Quick Start 18## Quick Start
diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml
new file mode 100644
index 000000000..646ee7fd5
--- /dev/null
+++ b/crates/paths/Cargo.toml
@@ -0,0 +1,8 @@
1[package]
2name = "paths"
3version = "0.1.0"
4authors = ["rust-analyzer developers"]
5edition = "2018"
6
7[lib]
8doctest = false
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs
new file mode 100644
index 000000000..c7ce0c42f
--- /dev/null
+++ b/crates/paths/src/lib.rs
@@ -0,0 +1,123 @@
1//! Thin wrappers around `std::path`, distinguishing between absolute and
2//! relative paths.
3use std::{
4 convert::{TryFrom, TryInto},
5 ops,
6 path::{Component, Path, PathBuf},
7};
8
9#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
10pub struct AbsPathBuf(PathBuf);
11
12impl From<AbsPathBuf> for PathBuf {
13 fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf {
14 path_buf
15 }
16}
17
18impl ops::Deref for AbsPathBuf {
19 type Target = AbsPath;
20 fn deref(&self) -> &AbsPath {
21 self.as_path()
22 }
23}
24
25impl AsRef<Path> for AbsPathBuf {
26 fn as_ref(&self) -> &Path {
27 self.0.as_path()
28 }
29}
30
31impl TryFrom<PathBuf> for AbsPathBuf {
32 type Error = PathBuf;
33 fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
34 if !path_buf.is_absolute() {
35 return Err(path_buf);
36 }
37 Ok(AbsPathBuf(path_buf))
38 }
39}
40
41impl TryFrom<&str> for AbsPathBuf {
42 type Error = PathBuf;
43 fn try_from(path: &str) -> Result<AbsPathBuf, PathBuf> {
44 AbsPathBuf::try_from(PathBuf::from(path))
45 }
46}
47
48impl AbsPathBuf {
49 pub fn as_path(&self) -> &AbsPath {
50 AbsPath::new_unchecked(self.0.as_path())
51 }
52 pub fn pop(&mut self) -> bool {
53 self.0.pop()
54 }
55}
56
57#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
58#[repr(transparent)]
59pub struct AbsPath(Path);
60
61impl ops::Deref for AbsPath {
62 type Target = Path;
63 fn deref(&self) -> &Path {
64 &self.0
65 }
66}
67
68impl AsRef<Path> for AbsPath {
69 fn as_ref(&self) -> &Path {
70 &self.0
71 }
72}
73
74impl<'a> TryFrom<&'a Path> for &'a AbsPath {
75 type Error = &'a Path;
76 fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> {
77 if !path.is_absolute() {
78 return Err(path);
79 }
80 Ok(AbsPath::new_unchecked(path))
81 }
82}
83
84impl AbsPath {
85 fn new_unchecked(path: &Path) -> &AbsPath {
86 unsafe { &*(path as *const Path as *const AbsPath) }
87 }
88
89 pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
90 self.as_ref().join(path).try_into().unwrap()
91 }
92 pub fn normalize(&self) -> AbsPathBuf {
93 AbsPathBuf(normalize_path(&self.0))
94 }
95}
96
97// https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85
98fn normalize_path(path: &Path) -> PathBuf {
99 let mut components = path.components().peekable();
100 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
101 components.next();
102 PathBuf::from(c.as_os_str())
103 } else {
104 PathBuf::new()
105 };
106
107 for component in components {
108 match component {
109 Component::Prefix(..) => unreachable!(),
110 Component::RootDir => {
111 ret.push(component.as_os_str());
112 }
113 Component::CurDir => {}
114 Component::ParentDir => {
115 ret.pop();
116 }
117 Component::Normal(c) => {
118 ret.push(c);
119 }
120 }
121 }
122 ret
123}
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index edd8255f4..ee614de72 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -252,7 +252,7 @@ impl AssistBuilder {
252 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { 252 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
253 let node = rewriter.rewrite_root().unwrap(); 253 let node = rewriter.rewrite_root().unwrap();
254 let new = rewriter.rewrite(&node); 254 let new = rewriter.rewrite(&node);
255 algo::diff(&node, &new).into_text_edit(&mut self.edit) 255 algo::diff(&node, &new).into_text_edit(&mut self.edit);
256 } 256 }
257 257
258 // FIXME: kill this API 258 // FIXME: kill this API
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 3079a02a2..00fa95b6c 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -106,6 +106,7 @@ impl<'a> SubstituteTypeParams<'a> {
106 _ => return None, 106 _ => return None,
107 }; 107 };
108 // FIXME: use `hir::Path::from_src` instead. 108 // FIXME: use `hir::Path::from_src` instead.
109 #[allow(deprecated)]
109 let path = hir::Path::from_ast(path)?; 110 let path = hir::Path::from_ast(path)?;
110 let resolution = self.source_scope.resolve_hir_path(&path)?; 111 let resolution = self.source_scope.resolve_hir_path(&path)?;
111 match resolution { 112 match resolution {
@@ -150,6 +151,7 @@ impl<'a> QualifyPaths<'a> {
150 return None; 151 return None;
151 } 152 }
152 // FIXME: use `hir::Path::from_src` instead. 153 // FIXME: use `hir::Path::from_src` instead.
154 #[allow(deprecated)]
153 let hir_path = hir::Path::from_ast(p.clone()); 155 let hir_path = hir::Path::from_ast(p.clone());
154 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; 156 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
155 match resolution { 157 match resolution {
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index cc303285b..569efb768 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -136,8 +136,20 @@ fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
136} 136}
137 137
138fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { 138fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
139 let pat_head = pat.syntax().first_child().map(|node| node.text()); 139 let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text());
140 let var_head = var.syntax().first_child().map(|node| node.text()); 140
141 let pat_head = match pat {
142 Pat::BindPat(bind_pat) => {
143 if let Some(p) = bind_pat.pat() {
144 first_node_text(&p)
145 } else {
146 return false;
147 }
148 }
149 pat => first_node_text(pat),
150 };
151
152 let var_head = first_node_text(var);
141 153
142 pat_head == var_head 154 pat_head == var_head
143} 155}
@@ -351,6 +363,40 @@ mod tests {
351 } 363 }
352 364
353 #[test] 365 #[test]
366 fn partial_fill_bind_pat() {
367 check_assist(
368 fill_match_arms,
369 r#"
370 enum A {
371 As,
372 Bs,
373 Cs(Option<i32>),
374 }
375 fn main() {
376 match A::As<|> {
377 A::As(_) => {}
378 a @ A::Bs(_) => {}
379 }
380 }
381 "#,
382 r#"
383 enum A {
384 As,
385 Bs,
386 Cs(Option<i32>),
387 }
388 fn main() {
389 match A::As {
390 A::As(_) => {}
391 a @ A::Bs(_) => {}
392 $0A::Cs(_) => {}
393 }
394 }
395 "#,
396 );
397 }
398
399 #[test]
354 fn fill_match_arms_empty_body() { 400 fn fill_match_arms_empty_body() {
355 check_assist( 401 check_assist(
356 fill_match_arms, 402 fill_match_arms,
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
index 0197a8cf0..b4784c333 100644
--- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -1,7 +1,10 @@
1use hir; 1use hir;
2use ra_syntax::{ast, AstNode, SmolStr, TextRange}; 2use ra_syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SmolStr, SyntaxNode};
3 3
4use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists}; 4use crate::{
5 utils::{find_insert_use_container, insert_use_statement},
6 AssistContext, AssistId, Assists,
7};
5 8
6// Assist: replace_qualified_name_with_use 9// Assist: replace_qualified_name_with_use
7// 10//
@@ -39,16 +42,18 @@ pub(crate) fn replace_qualified_name_with_use(
39 target, 42 target,
40 |builder| { 43 |builder| {
41 let path_to_import = hir_path.mod_path().clone(); 44 let path_to_import = hir_path.mod_path().clone();
45 let container = match find_insert_use_container(path.syntax(), ctx) {
46 Some(c) => c,
47 None => return,
48 };
42 insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder()); 49 insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder());
43 50
44 if let Some(last) = path.segment() { 51 // Now that we've brought the name into scope, re-qualify all paths that could be
45 // Here we are assuming the assist will provide a correct use statement 52 // affected (that is, all paths inside the node we added the `use` to).
46 // so we can delete the path qualifier 53 let mut rewriter = SyntaxRewriter::default();
47 builder.delete(TextRange::new( 54 let syntax = container.either(|l| l.syntax().clone(), |r| r.syntax().clone());
48 path.syntax().text_range().start(), 55 shorten_paths(&mut rewriter, syntax, path);
49 last.syntax().text_range().start(), 56 builder.rewrite(rewriter);
50 ));
51 }
52 }, 57 },
53 ) 58 )
54} 59}
@@ -73,6 +78,69 @@ fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
73 Some(ps) 78 Some(ps)
74} 79}
75 80
81/// Adds replacements to `re` that shorten `path` in all descendants of `node`.
82fn shorten_paths(rewriter: &mut SyntaxRewriter<'static>, node: SyntaxNode, path: ast::Path) {
83 for child in node.children() {
84 match_ast! {
85 match child {
86 // Don't modify `use` items, as this can break the `use` item when injecting a new
87 // import into the use tree.
88 ast::UseItem(_it) => continue,
89 // Don't descend into submodules, they don't have the same `use` items in scope.
90 ast::Module(_it) => continue,
91
92 ast::Path(p) => {
93 match maybe_replace_path(rewriter, p.clone(), path.clone()) {
94 Some(()) => {},
95 None => shorten_paths(rewriter, p.syntax().clone(), path.clone()),
96 }
97 },
98 _ => shorten_paths(rewriter, child, path.clone()),
99 }
100 }
101 }
102}
103
104fn maybe_replace_path(
105 rewriter: &mut SyntaxRewriter<'static>,
106 path: ast::Path,
107 target: ast::Path,
108) -> Option<()> {
109 if !path_eq(path.clone(), target.clone()) {
110 return None;
111 }
112
113 // Shorten `path`, leaving only its last segment.
114 if let Some(parent) = path.qualifier() {
115 rewriter.delete(parent.syntax());
116 }
117 if let Some(double_colon) = path.coloncolon_token() {
118 rewriter.delete(&double_colon);
119 }
120
121 Some(())
122}
123
124fn path_eq(lhs: ast::Path, rhs: ast::Path) -> bool {
125 let mut lhs_curr = lhs;
126 let mut rhs_curr = rhs;
127 loop {
128 match (lhs_curr.segment(), rhs_curr.segment()) {
129 (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
130 _ => return false,
131 }
132
133 match (lhs_curr.qualifier(), rhs_curr.qualifier()) {
134 (Some(lhs), Some(rhs)) => {
135 lhs_curr = lhs;
136 rhs_curr = rhs;
137 }
138 (None, None) => return true,
139 _ => return false,
140 }
141 }
142}
143
76#[cfg(test)] 144#[cfg(test)]
77mod tests { 145mod tests {
78 use crate::tests::{check_assist, check_assist_not_applicable}; 146 use crate::tests::{check_assist, check_assist_not_applicable};
@@ -83,10 +151,10 @@ mod tests {
83 fn test_replace_add_use_no_anchor() { 151 fn test_replace_add_use_no_anchor() {
84 check_assist( 152 check_assist(
85 replace_qualified_name_with_use, 153 replace_qualified_name_with_use,
86 " 154 r"
87std::fmt::Debug<|> 155std::fmt::Debug<|>
88 ", 156 ",
89 " 157 r"
90use std::fmt::Debug; 158use std::fmt::Debug;
91 159
92Debug 160Debug
@@ -97,13 +165,13 @@ Debug
97 fn test_replace_add_use_no_anchor_with_item_below() { 165 fn test_replace_add_use_no_anchor_with_item_below() {
98 check_assist( 166 check_assist(
99 replace_qualified_name_with_use, 167 replace_qualified_name_with_use,
100 " 168 r"
101std::fmt::Debug<|> 169std::fmt::Debug<|>
102 170
103fn main() { 171fn main() {
104} 172}
105 ", 173 ",
106 " 174 r"
107use std::fmt::Debug; 175use std::fmt::Debug;
108 176
109Debug 177Debug
@@ -118,13 +186,13 @@ fn main() {
118 fn test_replace_add_use_no_anchor_with_item_above() { 186 fn test_replace_add_use_no_anchor_with_item_above() {
119 check_assist( 187 check_assist(
120 replace_qualified_name_with_use, 188 replace_qualified_name_with_use,
121 " 189 r"
122fn main() { 190fn main() {
123} 191}
124 192
125std::fmt::Debug<|> 193std::fmt::Debug<|>
126 ", 194 ",
127 " 195 r"
128use std::fmt::Debug; 196use std::fmt::Debug;
129 197
130fn main() { 198fn main() {
@@ -139,10 +207,10 @@ Debug
139 fn test_replace_add_use_no_anchor_2seg() { 207 fn test_replace_add_use_no_anchor_2seg() {
140 check_assist( 208 check_assist(
141 replace_qualified_name_with_use, 209 replace_qualified_name_with_use,
142 " 210 r"
143std::fmt<|>::Debug 211std::fmt<|>::Debug
144 ", 212 ",
145 " 213 r"
146use std::fmt; 214use std::fmt;
147 215
148fmt::Debug 216fmt::Debug
@@ -154,13 +222,13 @@ fmt::Debug
154 fn test_replace_add_use() { 222 fn test_replace_add_use() {
155 check_assist( 223 check_assist(
156 replace_qualified_name_with_use, 224 replace_qualified_name_with_use,
157 " 225 r"
158use stdx; 226use stdx;
159 227
160impl std::fmt::Debug<|> for Foo { 228impl std::fmt::Debug<|> for Foo {
161} 229}
162 ", 230 ",
163 " 231 r"
164use stdx; 232use stdx;
165use std::fmt::Debug; 233use std::fmt::Debug;
166 234
@@ -174,11 +242,11 @@ impl Debug for Foo {
174 fn test_replace_file_use_other_anchor() { 242 fn test_replace_file_use_other_anchor() {
175 check_assist( 243 check_assist(
176 replace_qualified_name_with_use, 244 replace_qualified_name_with_use,
177 " 245 r"
178impl std::fmt::Debug<|> for Foo { 246impl std::fmt::Debug<|> for Foo {
179} 247}
180 ", 248 ",
181 " 249 r"
182use std::fmt::Debug; 250use std::fmt::Debug;
183 251
184impl Debug for Foo { 252impl Debug for Foo {
@@ -191,11 +259,11 @@ impl Debug for Foo {
191 fn test_replace_add_use_other_anchor_indent() { 259 fn test_replace_add_use_other_anchor_indent() {
192 check_assist( 260 check_assist(
193 replace_qualified_name_with_use, 261 replace_qualified_name_with_use,
194 " 262 r"
195 impl std::fmt::Debug<|> for Foo { 263 impl std::fmt::Debug<|> for Foo {
196 } 264 }
197 ", 265 ",
198 " 266 r"
199 use std::fmt::Debug; 267 use std::fmt::Debug;
200 268
201 impl Debug for Foo { 269 impl Debug for Foo {
@@ -208,13 +276,13 @@ impl Debug for Foo {
208 fn test_replace_split_different() { 276 fn test_replace_split_different() {
209 check_assist( 277 check_assist(
210 replace_qualified_name_with_use, 278 replace_qualified_name_with_use,
211 " 279 r"
212use std::fmt; 280use std::fmt;
213 281
214impl std::io<|> for Foo { 282impl std::io<|> for Foo {
215} 283}
216 ", 284 ",
217 " 285 r"
218use std::{io, fmt}; 286use std::{io, fmt};
219 287
220impl io for Foo { 288impl io for Foo {
@@ -227,13 +295,13 @@ impl io for Foo {
227 fn test_replace_split_self_for_use() { 295 fn test_replace_split_self_for_use() {
228 check_assist( 296 check_assist(
229 replace_qualified_name_with_use, 297 replace_qualified_name_with_use,
230 " 298 r"
231use std::fmt; 299use std::fmt;
232 300
233impl std::fmt::Debug<|> for Foo { 301impl std::fmt::Debug<|> for Foo {
234} 302}
235 ", 303 ",
236 " 304 r"
237use std::fmt::{self, Debug, }; 305use std::fmt::{self, Debug, };
238 306
239impl Debug for Foo { 307impl Debug for Foo {
@@ -246,13 +314,13 @@ impl Debug for Foo {
246 fn test_replace_split_self_for_target() { 314 fn test_replace_split_self_for_target() {
247 check_assist( 315 check_assist(
248 replace_qualified_name_with_use, 316 replace_qualified_name_with_use,
249 " 317 r"
250use std::fmt::Debug; 318use std::fmt::Debug;
251 319
252impl std::fmt<|> for Foo { 320impl std::fmt<|> for Foo {
253} 321}
254 ", 322 ",
255 " 323 r"
256use std::fmt::{self, Debug}; 324use std::fmt::{self, Debug};
257 325
258impl fmt for Foo { 326impl fmt for Foo {
@@ -265,13 +333,13 @@ impl fmt for Foo {
265 fn test_replace_add_to_nested_self_nested() { 333 fn test_replace_add_to_nested_self_nested() {
266 check_assist( 334 check_assist(
267 replace_qualified_name_with_use, 335 replace_qualified_name_with_use,
268 " 336 r"
269use std::fmt::{Debug, nested::{Display}}; 337use std::fmt::{Debug, nested::{Display}};
270 338
271impl std::fmt::nested<|> for Foo { 339impl std::fmt::nested<|> for Foo {
272} 340}
273", 341",
274 " 342 r"
275use std::fmt::{Debug, nested::{Display, self}}; 343use std::fmt::{Debug, nested::{Display, self}};
276 344
277impl nested for Foo { 345impl nested for Foo {
@@ -284,13 +352,13 @@ impl nested for Foo {
284 fn test_replace_add_to_nested_self_already_included() { 352 fn test_replace_add_to_nested_self_already_included() {
285 check_assist( 353 check_assist(
286 replace_qualified_name_with_use, 354 replace_qualified_name_with_use,
287 " 355 r"
288use std::fmt::{Debug, nested::{self, Display}}; 356use std::fmt::{Debug, nested::{self, Display}};
289 357
290impl std::fmt::nested<|> for Foo { 358impl std::fmt::nested<|> for Foo {
291} 359}
292", 360",
293 " 361 r"
294use std::fmt::{Debug, nested::{self, Display}}; 362use std::fmt::{Debug, nested::{self, Display}};
295 363
296impl nested for Foo { 364impl nested for Foo {
@@ -303,13 +371,13 @@ impl nested for Foo {
303 fn test_replace_add_to_nested_nested() { 371 fn test_replace_add_to_nested_nested() {
304 check_assist( 372 check_assist(
305 replace_qualified_name_with_use, 373 replace_qualified_name_with_use,
306 " 374 r"
307use std::fmt::{Debug, nested::{Display}}; 375use std::fmt::{Debug, nested::{Display}};
308 376
309impl std::fmt::nested::Debug<|> for Foo { 377impl std::fmt::nested::Debug<|> for Foo {
310} 378}
311", 379",
312 " 380 r"
313use std::fmt::{Debug, nested::{Display, Debug}}; 381use std::fmt::{Debug, nested::{Display, Debug}};
314 382
315impl Debug for Foo { 383impl Debug for Foo {
@@ -322,13 +390,13 @@ impl Debug for Foo {
322 fn test_replace_split_common_target_longer() { 390 fn test_replace_split_common_target_longer() {
323 check_assist( 391 check_assist(
324 replace_qualified_name_with_use, 392 replace_qualified_name_with_use,
325 " 393 r"
326use std::fmt::Debug; 394use std::fmt::Debug;
327 395
328impl std::fmt::nested::Display<|> for Foo { 396impl std::fmt::nested::Display<|> for Foo {
329} 397}
330", 398",
331 " 399 r"
332use std::fmt::{nested::Display, Debug}; 400use std::fmt::{nested::Display, Debug};
333 401
334impl Display for Foo { 402impl Display for Foo {
@@ -341,13 +409,13 @@ impl Display for Foo {
341 fn test_replace_split_common_use_longer() { 409 fn test_replace_split_common_use_longer() {
342 check_assist( 410 check_assist(
343 replace_qualified_name_with_use, 411 replace_qualified_name_with_use,
344 " 412 r"
345use std::fmt::nested::Debug; 413use std::fmt::nested::Debug;
346 414
347impl std::fmt::Display<|> for Foo { 415impl std::fmt::Display<|> for Foo {
348} 416}
349", 417",
350 " 418 r"
351use std::fmt::{Display, nested::Debug}; 419use std::fmt::{Display, nested::Debug};
352 420
353impl Display for Foo { 421impl Display for Foo {
@@ -360,7 +428,7 @@ impl Display for Foo {
360 fn test_replace_use_nested_import() { 428 fn test_replace_use_nested_import() {
361 check_assist( 429 check_assist(
362 replace_qualified_name_with_use, 430 replace_qualified_name_with_use,
363 " 431 r"
364use crate::{ 432use crate::{
365 ty::{Substs, Ty}, 433 ty::{Substs, Ty},
366 AssocItem, 434 AssocItem,
@@ -368,7 +436,7 @@ use crate::{
368 436
369fn foo() { crate::ty::lower<|>::trait_env() } 437fn foo() { crate::ty::lower<|>::trait_env() }
370", 438",
371 " 439 r"
372use crate::{ 440use crate::{
373 ty::{Substs, Ty, lower}, 441 ty::{Substs, Ty, lower},
374 AssocItem, 442 AssocItem,
@@ -383,13 +451,13 @@ fn foo() { lower::trait_env() }
383 fn test_replace_alias() { 451 fn test_replace_alias() {
384 check_assist( 452 check_assist(
385 replace_qualified_name_with_use, 453 replace_qualified_name_with_use,
386 " 454 r"
387use std::fmt as foo; 455use std::fmt as foo;
388 456
389impl foo::Debug<|> for Foo { 457impl foo::Debug<|> for Foo {
390} 458}
391", 459",
392 " 460 r"
393use std::fmt as foo; 461use std::fmt as foo;
394 462
395impl Debug for Foo { 463impl Debug for Foo {
@@ -402,7 +470,7 @@ impl Debug for Foo {
402 fn test_replace_not_applicable_one_segment() { 470 fn test_replace_not_applicable_one_segment() {
403 check_assist_not_applicable( 471 check_assist_not_applicable(
404 replace_qualified_name_with_use, 472 replace_qualified_name_with_use,
405 " 473 r"
406impl foo<|> for Foo { 474impl foo<|> for Foo {
407} 475}
408", 476",
@@ -413,7 +481,7 @@ impl foo<|> for Foo {
413 fn test_replace_not_applicable_in_use() { 481 fn test_replace_not_applicable_in_use() {
414 check_assist_not_applicable( 482 check_assist_not_applicable(
415 replace_qualified_name_with_use, 483 replace_qualified_name_with_use,
416 " 484 r"
417use std::fmt<|>; 485use std::fmt<|>;
418", 486",
419 ); 487 );
@@ -423,14 +491,14 @@ use std::fmt<|>;
423 fn test_replace_add_use_no_anchor_in_mod_mod() { 491 fn test_replace_add_use_no_anchor_in_mod_mod() {
424 check_assist( 492 check_assist(
425 replace_qualified_name_with_use, 493 replace_qualified_name_with_use,
426 " 494 r"
427mod foo { 495mod foo {
428 mod bar { 496 mod bar {
429 std::fmt::Debug<|> 497 std::fmt::Debug<|>
430 } 498 }
431} 499}
432 ", 500 ",
433 " 501 r"
434mod foo { 502mod foo {
435 mod bar { 503 mod bar {
436 use std::fmt::Debug; 504 use std::fmt::Debug;
@@ -446,14 +514,14 @@ mod foo {
446 fn inserts_imports_after_inner_attributes() { 514 fn inserts_imports_after_inner_attributes() {
447 check_assist( 515 check_assist(
448 replace_qualified_name_with_use, 516 replace_qualified_name_with_use,
449 " 517 r"
450#![allow(dead_code)] 518#![allow(dead_code)]
451 519
452fn main() { 520fn main() {
453 std::fmt::Debug<|> 521 std::fmt::Debug<|>
454} 522}
455 ", 523 ",
456 " 524 r"
457#![allow(dead_code)] 525#![allow(dead_code)]
458use std::fmt::Debug; 526use std::fmt::Debug;
459 527
@@ -463,4 +531,116 @@ fn main() {
463 ", 531 ",
464 ); 532 );
465 } 533 }
534
535 #[test]
536 fn replaces_all_affected_paths() {
537 check_assist(
538 replace_qualified_name_with_use,
539 r"
540fn main() {
541 std::fmt::Debug<|>;
542 let x: std::fmt::Debug = std::fmt::Debug;
543}
544 ",
545 r"
546use std::fmt::Debug;
547
548fn main() {
549 Debug;
550 let x: Debug = Debug;
551}
552 ",
553 );
554 }
555
556 #[test]
557 fn replaces_all_affected_paths_mod() {
558 check_assist(
559 replace_qualified_name_with_use,
560 r"
561mod m {
562 fn f() {
563 std::fmt::Debug<|>;
564 let x: std::fmt::Debug = std::fmt::Debug;
565 }
566 fn g() {
567 std::fmt::Debug;
568 }
569}
570
571fn f() {
572 std::fmt::Debug;
573}
574 ",
575 r"
576mod m {
577 use std::fmt::Debug;
578
579 fn f() {
580 Debug;
581 let x: Debug = Debug;
582 }
583 fn g() {
584 Debug;
585 }
586}
587
588fn f() {
589 std::fmt::Debug;
590}
591 ",
592 );
593 }
594
595 #[test]
596 fn does_not_replace_in_submodules() {
597 check_assist(
598 replace_qualified_name_with_use,
599 r"
600fn main() {
601 std::fmt::Debug<|>;
602}
603
604mod sub {
605 fn f() {
606 std::fmt::Debug;
607 }
608}
609 ",
610 r"
611use std::fmt::Debug;
612
613fn main() {
614 Debug;
615}
616
617mod sub {
618 fn f() {
619 std::fmt::Debug;
620 }
621}
622 ",
623 );
624 }
625
626 #[test]
627 fn does_not_replace_in_use() {
628 check_assist(
629 replace_qualified_name_with_use,
630 r"
631use std::fmt::Display;
632
633fn main() {
634 std::fmt<|>;
635}
636 ",
637 r"
638use std::fmt::{self, Display};
639
640fn main() {
641 fmt;
642}
643 ",
644 );
645 }
466} 646}
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs
index 0038a9764..c1ff0de7b 100644
--- a/crates/ra_assists/src/utils.rs
+++ b/crates/ra_assists/src/utils.rs
@@ -13,7 +13,7 @@ use rustc_hash::FxHashSet;
13 13
14use crate::assist_config::SnippetCap; 14use crate::assist_config::SnippetCap;
15 15
16pub(crate) use insert_use::insert_use_statement; 16pub(crate) use insert_use::{find_insert_use_container, insert_use_statement};
17 17
18#[derive(Clone, Copy, Debug)] 18#[derive(Clone, Copy, Debug)]
19pub(crate) enum Cursor<'a> { 19pub(crate) enum Cursor<'a> {
diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs
index 0ee43482f..8c4f33e59 100644
--- a/crates/ra_assists/src/utils/insert_use.rs
+++ b/crates/ra_assists/src/utils/insert_use.rs
@@ -12,6 +12,20 @@ use ra_syntax::{
12use ra_text_edit::TextEditBuilder; 12use ra_text_edit::TextEditBuilder;
13 13
14use crate::assist_context::AssistContext; 14use crate::assist_context::AssistContext;
15use either::Either;
16
17/// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
18pub(crate) fn find_insert_use_container(
19 position: &SyntaxNode,
20 ctx: &AssistContext,
21) -> Option<Either<ast::ItemList, ast::SourceFile>> {
22 ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| {
23 if let Some(module) = ast::Module::cast(n.clone()) {
24 return module.item_list().map(|it| Either::Left(it));
25 }
26 Some(Either::Right(ast::SourceFile::cast(n)?))
27 })
28}
15 29
16/// Creates and inserts a use statement for the given path to import. 30/// Creates and inserts a use statement for the given path to import.
17/// The use statement is inserted in the scope most appropriate to the 31/// The use statement is inserted in the scope most appropriate to the
@@ -24,15 +38,11 @@ pub(crate) fn insert_use_statement(
24 builder: &mut TextEditBuilder, 38 builder: &mut TextEditBuilder,
25) { 39) {
26 let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>(); 40 let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>();
27 let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| { 41 let container = find_insert_use_container(position, ctx);
28 if let Some(module) = ast::Module::cast(n.clone()) {
29 return module.item_list().map(|it| it.syntax().clone());
30 }
31 ast::SourceFile::cast(n).map(|it| it.syntax().clone())
32 });
33 42
34 if let Some(container) = container { 43 if let Some(container) = container {
35 let action = best_action_for_target(container, position.clone(), &target); 44 let syntax = container.either(|l| l.syntax().clone(), |r| r.syntax().clone());
45 let action = best_action_for_target(syntax, position.clone(), &target);
36 make_assist(&action, &target, builder); 46 make_assist(&action, &target, builder);
37 } 47 }
38} 48}
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
index 510c5e064..30db48f86 100644
--- a/crates/ra_hir_def/src/diagnostics.rs
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -3,7 +3,6 @@
3use std::any::Any; 3use std::any::Any;
4 4
5use hir_expand::diagnostics::Diagnostic; 5use hir_expand::diagnostics::Diagnostic;
6use ra_db::RelativePathBuf;
7use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; 6use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
8 7
9use hir_expand::{HirFileId, InFile}; 8use hir_expand::{HirFileId, InFile};
@@ -12,7 +11,7 @@ use hir_expand::{HirFileId, InFile};
12pub struct UnresolvedModule { 11pub struct UnresolvedModule {
13 pub file: HirFileId, 12 pub file: HirFileId,
14 pub decl: AstPtr<ast::Module>, 13 pub decl: AstPtr<ast::Module>,
15 pub candidate: RelativePathBuf, 14 pub candidate: String,
16} 15}
17 16
18impl Diagnostic for UnresolvedModule { 17impl Diagnostic for UnresolvedModule {
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index a7f59e028..06701a830 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -159,10 +159,16 @@ fn find_path_inner(
159 let crate_graph = db.crate_graph(); 159 let crate_graph = db.crate_graph();
160 let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { 160 let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
161 let import_map = db.import_map(dep.crate_id); 161 let import_map = db.import_map(dep.crate_id);
162 import_map.path_of(item).map(|modpath| { 162 import_map.import_info_for(item).and_then(|info| {
163 let mut modpath = modpath.clone(); 163 // Determine best path for containing module and append last segment from `info`.
164 modpath.segments.insert(0, dep.as_name()); 164 let mut path = find_path_inner(
165 modpath 165 db,
166 ItemInNs::Types(ModuleDefId::ModuleId(info.container)),
167 from,
168 best_path_len - 1,
169 )?;
170 path.segments.push(info.path.segments.last().unwrap().clone());
171 Some(path)
166 }) 172 })
167 }); 173 });
168 174
@@ -299,8 +305,8 @@ mod tests {
299 /// `code` needs to contain a cursor marker; checks that `find_path` for the 305 /// `code` needs to contain a cursor marker; checks that `find_path` for the
300 /// item the `path` refers to returns that same path when called from the 306 /// item the `path` refers to returns that same path when called from the
301 /// module the cursor is in. 307 /// module the cursor is in.
302 fn check_found_path(code: &str, path: &str) { 308 fn check_found_path(ra_fixture: &str, path: &str) {
303 let (db, pos) = TestDB::with_position(code); 309 let (db, pos) = TestDB::with_position(ra_fixture);
304 let module = db.module_for_file(pos.file_id); 310 let module = db.module_for_file(pos.file_id);
305 let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); 311 let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
306 let ast_path = parsed_path_file 312 let ast_path = parsed_path_file
@@ -420,7 +426,6 @@ mod tests {
420 426
421 #[test] 427 #[test]
422 fn different_crate_renamed() { 428 fn different_crate_renamed() {
423 // Even if a local path exists, if the item is defined externally, prefer an external path.
424 let code = r#" 429 let code = r#"
425 //- /main.rs crate:main deps:std 430 //- /main.rs crate:main deps:std
426 extern crate std as std_renamed; 431 extern crate std as std_renamed;
@@ -428,7 +433,45 @@ mod tests {
428 //- /std.rs crate:std 433 //- /std.rs crate:std
429 pub struct S; 434 pub struct S;
430 "#; 435 "#;
431 check_found_path(code, "std::S"); 436 check_found_path(code, "std_renamed::S");
437 }
438
439 #[test]
440 fn partially_imported() {
441 // Tests that short paths are used even for external items, when parts of the path are
442 // already in scope.
443 check_found_path(
444 r#"
445 //- /main.rs crate:main deps:ra_syntax
446
447 use ra_syntax::ast;
448 <|>
449
450 //- /lib.rs crate:ra_syntax
451 pub mod ast {
452 pub enum ModuleItem {
453 A, B, C,
454 }
455 }
456 "#,
457 "ast::ModuleItem",
458 );
459
460 check_found_path(
461 r#"
462 //- /main.rs crate:main deps:ra_syntax
463
464 <|>
465
466 //- /lib.rs crate:ra_syntax
467 pub mod ast {
468 pub enum ModuleItem {
469 A, B, C,
470 }
471 }
472 "#,
473 "ra_syntax::ast::ModuleItem",
474 );
432 } 475 }
433 476
434 #[test] 477 #[test]
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs
index 36b4fdd81..68e20d06b 100644
--- a/crates/ra_hir_def/src/import_map.rs
+++ b/crates/ra_hir_def/src/import_map.rs
@@ -17,6 +17,15 @@ use crate::{
17 17
18type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; 18type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
19 19
20/// Item import details stored in the `ImportMap`.
21#[derive(Debug, Clone, Eq, PartialEq)]
22pub struct ImportInfo {
23 /// A path that can be used to import the item, relative to the crate's root.
24 pub path: ModPath,
25 /// The module containing this item.
26 pub container: ModuleId,
27}
28
20/// A map from publicly exported items to the path needed to import/name them from a downstream 29/// A map from publicly exported items to the path needed to import/name them from a downstream
21/// crate. 30/// crate.
22/// 31///
@@ -26,7 +35,7 @@ type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
26/// Note that all paths are relative to the containing crate's root, so the crate name still needs 35/// Note that all paths are relative to the containing crate's root, so the crate name still needs
27/// to be prepended to the `ModPath` before the path is valid. 36/// to be prepended to the `ModPath` before the path is valid.
28pub struct ImportMap { 37pub struct ImportMap {
29 map: FxIndexMap<ItemInNs, ModPath>, 38 map: FxIndexMap<ItemInNs, ImportInfo>,
30 39
31 /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the 40 /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
32 /// values returned by running `fst`. 41 /// values returned by running `fst`.
@@ -78,12 +87,12 @@ impl ImportMap {
78 let path = mk_path(); 87 let path = mk_path();
79 match import_map.entry(item) { 88 match import_map.entry(item) {
80 Entry::Vacant(entry) => { 89 Entry::Vacant(entry) => {
81 entry.insert(path); 90 entry.insert(ImportInfo { path, container: module });
82 } 91 }
83 Entry::Occupied(mut entry) => { 92 Entry::Occupied(mut entry) => {
84 // If the new path is shorter, prefer that one. 93 // If the new path is shorter, prefer that one.
85 if path.len() < entry.get().len() { 94 if path.len() < entry.get().path.len() {
86 *entry.get_mut() = path; 95 *entry.get_mut() = ImportInfo { path, container: module };
87 } else { 96 } else {
88 continue; 97 continue;
89 } 98 }
@@ -119,7 +128,7 @@ impl ImportMap {
119 let start = last_batch_start; 128 let start = last_batch_start;
120 last_batch_start = idx + 1; 129 last_batch_start = idx + 1;
121 130
122 let key = fst_path(&importables[start].1); 131 let key = fst_path(&importables[start].1.path);
123 132
124 builder.insert(key, start as u64).unwrap(); 133 builder.insert(key, start as u64).unwrap();
125 } 134 }
@@ -132,6 +141,10 @@ impl ImportMap {
132 141
133 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. 142 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
134 pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> { 143 pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> {
144 Some(&self.map.get(&item)?.path)
145 }
146
147 pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
135 self.map.get(&item) 148 self.map.get(&item)
136 } 149 }
137} 150}
@@ -150,13 +163,13 @@ impl fmt::Debug for ImportMap {
150 let mut importable_paths: Vec<_> = self 163 let mut importable_paths: Vec<_> = self
151 .map 164 .map
152 .iter() 165 .iter()
153 .map(|(item, modpath)| { 166 .map(|(item, info)| {
154 let ns = match item { 167 let ns = match item {
155 ItemInNs::Types(_) => "t", 168 ItemInNs::Types(_) => "t",
156 ItemInNs::Values(_) => "v", 169 ItemInNs::Values(_) => "v",
157 ItemInNs::Macros(_) => "m", 170 ItemInNs::Macros(_) => "m",
158 }; 171 };
159 format!("- {} ({})", modpath, ns) 172 format!("- {} ({})", info.path, ns)
160 }) 173 })
161 .collect(); 174 .collect();
162 175
@@ -171,9 +184,9 @@ fn fst_path(path: &ModPath) -> String {
171 s 184 s
172} 185}
173 186
174fn cmp((_, lhs): &(&ItemInNs, &ModPath), (_, rhs): &(&ItemInNs, &ModPath)) -> Ordering { 187fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering {
175 let lhs_str = fst_path(lhs); 188 let lhs_str = fst_path(&lhs.path);
176 let rhs_str = fst_path(rhs); 189 let rhs_str = fst_path(&rhs.path);
177 lhs_str.cmp(&rhs_str) 190 lhs_str.cmp(&rhs_str)
178} 191}
179 192
@@ -243,7 +256,7 @@ pub fn search_dependencies<'a>(
243 let importables = &import_map.importables[indexed_value.value as usize..]; 256 let importables = &import_map.importables[indexed_value.value as usize..];
244 257
245 // Path shared by the importable items in this group. 258 // Path shared by the importable items in this group.
246 let path = &import_map.map[&importables[0]]; 259 let path = &import_map.map[&importables[0]].path;
247 260
248 if query.anchor_end { 261 if query.anchor_end {
249 // Last segment must match query. 262 // Last segment must match query.
@@ -256,14 +269,14 @@ pub fn search_dependencies<'a>(
256 // Add the items from this `ModPath` group. Those are all subsequent items in 269 // Add the items from this `ModPath` group. Those are all subsequent items in
257 // `importables` whose paths match `path`. 270 // `importables` whose paths match `path`.
258 let iter = importables.iter().copied().take_while(|item| { 271 let iter = importables.iter().copied().take_while(|item| {
259 let item_path = &import_map.map[item]; 272 let item_path = &import_map.map[item].path;
260 fst_path(item_path) == fst_path(path) 273 fst_path(item_path) == fst_path(path)
261 }); 274 });
262 275
263 if query.case_sensitive { 276 if query.case_sensitive {
264 // FIXME: This does not do a subsequence match. 277 // FIXME: This does not do a subsequence match.
265 res.extend(iter.filter(|item| { 278 res.extend(iter.filter(|item| {
266 let item_path = &import_map.map[item]; 279 let item_path = &import_map.map[item].path;
267 item_path.to_string().contains(&query.query) 280 item_path.to_string().contains(&query.query)
268 })); 281 }));
269 } else { 282 } else {
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index f279c2ad4..b3e5f491a 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -296,7 +296,6 @@ pub enum ModuleSource {
296 296
297mod diagnostics { 297mod diagnostics {
298 use hir_expand::diagnostics::DiagnosticSink; 298 use hir_expand::diagnostics::DiagnosticSink;
299 use ra_db::RelativePathBuf;
300 use ra_syntax::{ast, AstPtr}; 299 use ra_syntax::{ast, AstPtr};
301 300
302 use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId}; 301 use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId};
@@ -306,7 +305,7 @@ mod diagnostics {
306 UnresolvedModule { 305 UnresolvedModule {
307 module: LocalModuleId, 306 module: LocalModuleId,
308 declaration: AstId<ast::Module>, 307 declaration: AstId<ast::Module>,
309 candidate: RelativePathBuf, 308 candidate: String,
310 }, 309 },
311 } 310 }
312 311
diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs
index cede4a6fc..19fe0615a 100644
--- a/crates/ra_hir_def/src/nameres/mod_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs
@@ -44,7 +44,7 @@ impl ModDir {
44 file_id: HirFileId, 44 file_id: HirFileId,
45 name: &Name, 45 name: &Name,
46 attr_path: Option<&SmolStr>, 46 attr_path: Option<&SmolStr>,
47 ) -> Result<(FileId, ModDir), RelativePathBuf> { 47 ) -> Result<(FileId, ModDir), String> {
48 let file_id = file_id.original_file(db.upcast()); 48 let file_id = file_id.original_file(db.upcast());
49 49
50 let mut candidate_files = Vec::new(); 50 let mut candidate_files = Vec::new();
@@ -52,11 +52,11 @@ impl ModDir {
52 Some(attr_path) => { 52 Some(attr_path) => {
53 let base = 53 let base =
54 if self.root_non_dir_owner { self.path.parent().unwrap() } else { &self.path }; 54 if self.root_non_dir_owner { self.path.parent().unwrap() } else { &self.path };
55 candidate_files.push(base.join(attr_path)) 55 candidate_files.push(base.join(attr_path).to_string())
56 } 56 }
57 None => { 57 None => {
58 candidate_files.push(self.path.join(&format!("{}.rs", name))); 58 candidate_files.push(self.path.join(&format!("{}.rs", name)).to_string());
59 candidate_files.push(self.path.join(&format!("{}/mod.rs", name))); 59 candidate_files.push(self.path.join(&format!("{}/mod.rs", name)).to_string());
60 } 60 }
61 }; 61 };
62 62
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index ba16442bd..190d6d98d 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -154,7 +154,7 @@ pub enum GenericArg {
154 154
155impl Path { 155impl Path {
156 /// Converts an `ast::Path` to `Path`. Works with use trees. 156 /// Converts an `ast::Path` to `Path`. Works with use trees.
157 /// DEPRECATED: It does not handle `$crate` from macro call. 157 #[deprecated = "Doesn't handle hygiene, don't add new calls, remove old ones"]
158 pub fn from_ast(path: ast::Path) -> Option<Path> { 158 pub fn from_ast(path: ast::Path) -> Option<Path> {
159 lower::lower_path(path, &Hygiene::new_unhygienic()) 159 lower::lower_path(path, &Hygiene::new_unhygienic())
160 } 160 }
diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs
index 3e6e1e333..02a7a61f1 100644
--- a/crates/ra_hir_ty/src/_match.rs
+++ b/crates/ra_hir_ty/src/_match.rs
@@ -8,11 +8,11 @@
8//! This file includes the logic for exhaustiveness and usefulness checking for 8//! This file includes the logic for exhaustiveness and usefulness checking for
9//! pattern-matching. Specifically, given a list of patterns for a type, we can 9//! pattern-matching. Specifically, given a list of patterns for a type, we can
10//! tell whether: 10//! tell whether:
11//! (a) the patterns cover every possible constructor for the type [exhaustiveness] 11//! - (a) the patterns cover every possible constructor for the type (exhaustiveness).
12//! (b) each pattern is necessary [usefulness] 12//! - (b) each pattern is necessary (usefulness).
13//! 13//!
14//! The algorithm implemented here is a modified version of the one described in: 14//! The algorithm implemented here is a modified version of the one described in
15//! http://moscova.inria.fr/~maranget/papers/warn/index.html 15//! <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
16//! However, to save future implementors from reading the original paper, we 16//! However, to save future implementors from reading the original paper, we
17//! summarise the algorithm here to hopefully save time and be a little clearer 17//! summarise the algorithm here to hopefully save time and be a little clearer
18//! (without being so rigorous). 18//! (without being so rigorous).
@@ -37,20 +37,26 @@
37//! new pattern `p`. 37//! new pattern `p`.
38//! 38//!
39//! For example, say we have the following: 39//! For example, say we have the following:
40//!
41//! ```ignore
42//! // x: (Option<bool>, Result<()>)
43//! match x {
44//! (Some(true), _) => {}
45//! (None, Err(())) => {}
46//! (None, Err(_)) => {}
47//! }
40//! ``` 48//! ```
41//! // x: (Option<bool>, Result<()>) 49//!
42//! match x {
43//! (Some(true), _) => {}
44//! (None, Err(())) => {}
45//! (None, Err(_)) => {}
46//! }
47//! ```
48//! Here, the matrix `P` starts as: 50//! Here, the matrix `P` starts as:
51//!
52//! ```text
49//! [ 53//! [
50//! [(Some(true), _)], 54//! [(Some(true), _)],
51//! [(None, Err(()))], 55//! [(None, Err(()))],
52//! [(None, Err(_))], 56//! [(None, Err(_))],
53//! ] 57//! ]
58//! ```
59//!
54//! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering 60//! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering
55//! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because 61//! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because
56//! all the values it covers are already covered by row 2. 62//! all the values it covers are already covered by row 2.
@@ -60,53 +66,61 @@
60//! To match the paper, the top of the stack is at the beginning / on the left. 66//! To match the paper, the top of the stack is at the beginning / on the left.
61//! 67//!
62//! There are two important operations on pattern-stacks necessary to understand the algorithm: 68//! There are two important operations on pattern-stacks necessary to understand the algorithm:
63//! 1. We can pop a given constructor off the top of a stack. This operation is called
64//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or
65//! `None`) and `p` a pattern-stack.
66//! If the pattern on top of the stack can cover `c`, this removes the constructor and
67//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns.
68//! Otherwise the pattern-stack is discarded.
69//! This essentially filters those pattern-stacks whose top covers the constructor `c` and
70//! discards the others.
71//! 69//!
72//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we 70//! 1. We can pop a given constructor off the top of a stack. This operation is called
73//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the 71//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or
74//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get 72//! `None`) and `p` a pattern-stack.
75//! nothing back. 73//! If the pattern on top of the stack can cover `c`, this removes the constructor and
74//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns.
75//! Otherwise the pattern-stack is discarded.
76//! This essentially filters those pattern-stacks whose top covers the constructor `c` and
77//! discards the others.
78//!
79//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we
80//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the
81//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get
82//! nothing back.
83//!
84//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1`
85//! on top of the stack, and we have four cases:
86//!
87//! * 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We push onto
88//! the stack the arguments of this constructor, and return the result:
76//! 89//!
77//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1` 90//! r_1, .., r_a, p_2, .., p_n
78//! on top of the stack, and we have four cases:
79//! 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We
80//! push onto the stack the arguments of this constructor, and return the result:
81//! r_1, .., r_a, p_2, .., p_n
82//! 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and
83//! return nothing.
84//! 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has
85//! arguments (its arity), and return the resulting stack:
86//! _, .., _, p_2, .., p_n
87//! 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
88//! stack:
89//! S(c, (r_1, p_2, .., p_n))
90//! S(c, (r_2, p_2, .., p_n))
91//! 91//!
92//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is 92//! * 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and return
93//! a pattern-stack. 93//! nothing.
94//! This is used when we know there are missing constructor cases, but there might be 94//! * 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has
95//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check 95//! arguments (its arity), and return the resulting stack:
96//! all its *other* components.
97//! 96//!
98//! It is computed as follows. We look at the pattern `p_1` on top of the stack, 97//! _, .., _, p_2, .., p_n
99//! and we have three cases:
100//! 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
101//! 1.2. `p_1 = _`. We return the rest of the stack:
102//! p_2, .., p_n
103//! 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
104//! stack.
105//! D((r_1, p_2, .., p_n))
106//! D((r_2, p_2, .., p_n))
107//! 98//!
108//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the 99//! * 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting stack:
109//! exhaustive integer matching rules, so they're written here for posterity. 100//!
101//! S(c, (r_1, p_2, .., p_n))
102//! S(c, (r_2, p_2, .., p_n))
103//!
104//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is
105//! a pattern-stack.
106//! This is used when we know there are missing constructor cases, but there might be
107//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check
108//! all its *other* components.
109//!
110//! It is computed as follows. We look at the pattern `p_1` on top of the stack,
111//! and we have three cases:
112//! * 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
113//! * 1.2. `p_1 = _`. We return the rest of the stack:
114//!
115//! p_2, .., p_n
116//!
117//! * 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting stack:
118//!
119//! D((r_1, p_2, .., p_n))
120//! D((r_2, p_2, .., p_n))
121//!
122//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the
123//! exhaustive integer matching rules, so they're written here for posterity.
110//! 124//!
111//! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by 125//! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by
112//! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with 126//! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with
@@ -120,73 +134,88 @@
120//! operates principally on the first component of the matrix and new pattern-stack `p`. 134//! operates principally on the first component of the matrix and new pattern-stack `p`.
121//! This algorithm is realised in the `is_useful` function. 135//! This algorithm is realised in the `is_useful` function.
122//! 136//!
123//! Base case. (`n = 0`, i.e., an empty tuple pattern) 137//! Base case (`n = 0`, i.e., an empty tuple pattern):
124//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), 138//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), then
125//! then `U(P, p)` is false. 139//! `U(P, p)` is false.
126//! - Otherwise, `P` must be empty, so `U(P, p)` is true. 140//! - Otherwise, `P` must be empty, so `U(P, p)` is true.
141//!
142//! Inductive step (`n > 0`, i.e., whether there's at least one column [which may then be expanded
143//! into further columns later]). We're going to match on the top of the new pattern-stack, `p_1`:
144//!
145//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern.
146//! Then, the usefulness of `p_1` can be reduced to whether it is useful when
147//! we ignore all the patterns in the first column of `P` that involve other constructors.
148//! This is where `S(c, P)` comes in:
127//! 149//!
128//! Inductive step. (`n > 0`, i.e., whether there's at least one column 150//! ```text
129//! [which may then be expanded into further columns later]) 151//! U(P, p) := U(S(c, P), S(c, p))
130//! We're going to match on the top of the new pattern-stack, `p_1`. 152//! ```
131//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern.
132//! Then, the usefulness of `p_1` can be reduced to whether it is useful when
133//! we ignore all the patterns in the first column of `P` that involve other constructors.
134//! This is where `S(c, P)` comes in:
135//! `U(P, p) := U(S(c, P), S(c, p))`
136//! This special case is handled in `is_useful_specialized`.
137//! 153//!
138//! For example, if `P` is: 154//! This special case is handled in `is_useful_specialized`.
139//! [
140//! [Some(true), _],
141//! [None, 0],
142//! ]
143//! and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only
144//! matches values that row 2 doesn't. For row 1 however, we need to dig into the
145//! arguments of `Some` to know whether some new value is covered. So we compute
146//! `U([[true, _]], [false, 0])`.
147//! 155//!
148//! - If `p_1 == _`, then we look at the list of constructors that appear in the first 156//! For example, if `P` is:
149//! component of the rows of `P`:
150//! + If there are some constructors that aren't present, then we might think that the
151//! wildcard `_` is useful, since it covers those constructors that weren't covered
152//! before.
153//! That's almost correct, but only works if there were no wildcards in those first
154//! components. So we need to check that `p` is useful with respect to the rows that
155//! start with a wildcard, if there are any. This is where `D` comes in:
156//! `U(P, p) := U(D(P), D(p))`
157//! 157//!
158//! For example, if `P` is: 158//! ```text
159//! [ 159//! [
160//! [_, true, _], 160//! [Some(true), _],
161//! [None, false, 1], 161//! [None, 0],
162//! ] 162//! ]
163//! and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we 163//! ```
164//! only had row 2, we'd know that `p` is useful. However row 1 starts with a
165//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`.
166//! 164//!
167//! + Otherwise, all possible constructors (for the relevant type) are present. In this 165//! and `p` is `[Some(false), 0]`, then we don't care about row 2 since we know `p` only
168//! case we must check whether the wildcard pattern covers any unmatched value. For 166//! matches values that row 2 doesn't. For row 1 however, we need to dig into the
169//! that, we can think of the `_` pattern as a big OR-pattern that covers all 167//! arguments of `Some` to know whether some new value is covered. So we compute
170//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for 168//! `U([[true, _]], [false, 0])`.
171//! example. The wildcard pattern is useful in this case if it is useful when
172//! specialized to one of the possible constructors. So we compute:
173//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))`
174//! 169//!
175//! For example, if `P` is: 170//! - If `p_1 == _`, then we look at the list of constructors that appear in the first component of
176//! [ 171//! the rows of `P`:
177//! [Some(true), _], 172//! - If there are some constructors that aren't present, then we might think that the
178//! [None, false], 173//! wildcard `_` is useful, since it covers those constructors that weren't covered
179//! ] 174//! before.
180//! and `p` is [_, false], both `None` and `Some` constructors appear in the first 175//! That's almost correct, but only works if there were no wildcards in those first
181//! components of `P`. We will therefore try popping both constructors in turn: we 176//! components. So we need to check that `p` is useful with respect to the rows that
182//! compute U([[true, _]], [_, false]) for the `Some` constructor, and U([[false]], 177//! start with a wildcard, if there are any. This is where `D` comes in:
183//! [false]) for the `None` constructor. The first case returns true, so we know that 178//! `U(P, p) := U(D(P), D(p))`
184//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched
185//! before.
186//! 179//!
187//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately: 180//! For example, if `P` is:
188//! `U(P, p) := U(P, (r_1, p_2, .., p_n)) 181//! ```text
189//! || U(P, (r_2, p_2, .., p_n))` 182//! [
183//! [_, true, _],
184//! [None, false, 1],
185//! ]
186//! ```
187//! and `p` is `[_, false, _]`, the `Some` constructor doesn't appear in `P`. So if we
188//! only had row 2, we'd know that `p` is useful. However row 1 starts with a
189//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`.
190//!
191//! - Otherwise, all possible constructors (for the relevant type) are present. In this
192//! case we must check whether the wildcard pattern covers any unmatched value. For
193//! that, we can think of the `_` pattern as a big OR-pattern that covers all
194//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for
195//! example. The wildcard pattern is useful in this case if it is useful when
196//! specialized to one of the possible constructors. So we compute:
197//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))`
198//!
199//! For example, if `P` is:
200//! ```text
201//! [
202//! [Some(true), _],
203//! [None, false],
204//! ]
205//! ```
206//! and `p` is `[_, false]`, both `None` and `Some` constructors appear in the first
207//! components of `P`. We will therefore try popping both constructors in turn: we
208//! compute `U([[true, _]], [_, false])` for the `Some` constructor, and `U([[false]],
209//! [false])` for the `None` constructor. The first case returns true, so we know that
210//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched
211//! before.
212//!
213//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately:
214//!
215//! ```text
216//! U(P, p) := U(P, (r_1, p_2, .., p_n))
217//! || U(P, (r_2, p_2, .., p_n))
218//! ```
190use std::sync::Arc; 219use std::sync::Arc;
191 220
192use smallvec::{smallvec, SmallVec}; 221use smallvec::{smallvec, SmallVec};
@@ -333,7 +362,12 @@ impl PatStack {
333 cx: &MatchCheckCtx, 362 cx: &MatchCheckCtx,
334 constructor: &Constructor, 363 constructor: &Constructor,
335 ) -> MatchCheckResult<Option<PatStack>> { 364 ) -> MatchCheckResult<Option<PatStack>> {
336 let result = match (self.head().as_pat(cx), constructor) { 365 if self.is_empty() {
366 return Ok(None);
367 }
368
369 let head_pat = self.head().as_pat(cx);
370 let result = match (head_pat, constructor) {
337 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => { 371 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => {
338 if ellipsis.is_some() { 372 if ellipsis.is_some() {
339 // If there are ellipsis here, we should add the correct number of 373 // If there are ellipsis here, we should add the correct number of
@@ -502,7 +536,7 @@ impl Matrix {
502 } 536 }
503 537
504 fn heads(&self) -> Vec<PatIdOrWild> { 538 fn heads(&self) -> Vec<PatIdOrWild> {
505 self.0.iter().map(|p| p.head()).collect() 539 self.0.iter().flat_map(|p| p.get_head()).collect()
506 } 540 }
507 541
508 /// Computes `D(self)` for each contained PatStack. 542 /// Computes `D(self)` for each contained PatStack.
@@ -808,194 +842,193 @@ mod tests {
808 842
809 pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB}; 843 pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB};
810 844
811 pub(super) fn check_diagnostic_message(content: &str) -> String { 845 pub(super) fn check_diagnostic_message(ra_fixture: &str) -> String {
812 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().0 846 TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().0
813 } 847 }
814 848
815 pub(super) fn check_diagnostic(content: &str) { 849 pub(super) fn check_diagnostic(ra_fixture: &str) {
816 let diagnostic_count = 850 let diagnostic_count =
817 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().1; 851 TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1;
818 852
819 assert_eq!(1, diagnostic_count, "no diagnostic reported"); 853 assert_eq!(1, diagnostic_count, "no diagnostic reported");
820 } 854 }
821 855
822 pub(super) fn check_no_diagnostic(content: &str) { 856 pub(super) fn check_no_diagnostic(ra_fixture: &str) {
823 let diagnostic_count = 857 let diagnostic_count =
824 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().1; 858 TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1;
825 859
826 assert_eq!(0, diagnostic_count, "expected no diagnostic, found one"); 860 assert_eq!(0, diagnostic_count, "expected no diagnostic, found one");
827 } 861 }
828 862
829 #[test] 863 #[test]
830 fn empty_tuple_no_arms_diagnostic_message() { 864 fn empty_tuple_no_arms_diagnostic_message() {
831 let content = r"
832 fn test_fn() {
833 match () {
834 }
835 }
836 ";
837
838 assert_snapshot!( 865 assert_snapshot!(
839 check_diagnostic_message(content), 866 check_diagnostic_message(r"
867 fn test_fn() {
868 match () {
869 }
870 }
871 "),
840 @"\"()\": Missing match arm\n" 872 @"\"()\": Missing match arm\n"
841 ); 873 );
842 } 874 }
843 875
844 #[test] 876 #[test]
845 fn empty_tuple_no_arms() { 877 fn empty_tuple_no_arms() {
846 let content = r" 878 check_diagnostic(
879 r"
847 fn test_fn() { 880 fn test_fn() {
848 match () { 881 match () {
849 } 882 }
850 } 883 }
851 "; 884 ",
852 885 );
853 check_diagnostic(content);
854 } 886 }
855 887
856 #[test] 888 #[test]
857 fn empty_tuple_wild() { 889 fn empty_tuple_wild() {
858 let content = r" 890 check_no_diagnostic(
891 r"
859 fn test_fn() { 892 fn test_fn() {
860 match () { 893 match () {
861 _ => {} 894 _ => {}
862 } 895 }
863 } 896 }
864 "; 897 ",
865 898 );
866 check_no_diagnostic(content);
867 } 899 }
868 900
869 #[test] 901 #[test]
870 fn empty_tuple_no_diagnostic() { 902 fn empty_tuple_no_diagnostic() {
871 let content = r" 903 check_no_diagnostic(
904 r"
872 fn test_fn() { 905 fn test_fn() {
873 match () { 906 match () {
874 () => {} 907 () => {}
875 } 908 }
876 } 909 }
877 "; 910 ",
878 911 );
879 check_no_diagnostic(content);
880 } 912 }
881 913
882 #[test] 914 #[test]
883 fn tuple_of_empty_tuple_no_arms() { 915 fn tuple_of_empty_tuple_no_arms() {
884 let content = r" 916 check_diagnostic(
917 r"
885 fn test_fn() { 918 fn test_fn() {
886 match (()) { 919 match (()) {
887 } 920 }
888 } 921 }
889 "; 922 ",
890 923 );
891 check_diagnostic(content);
892 } 924 }
893 925
894 #[test] 926 #[test]
895 fn tuple_of_empty_tuple_no_diagnostic() { 927 fn tuple_of_empty_tuple_no_diagnostic() {
896 let content = r" 928 check_no_diagnostic(
929 r"
897 fn test_fn() { 930 fn test_fn() {
898 match (()) { 931 match (()) {
899 (()) => {} 932 (()) => {}
900 } 933 }
901 } 934 }
902 "; 935 ",
903 936 );
904 check_no_diagnostic(content);
905 } 937 }
906 938
907 #[test] 939 #[test]
908 fn tuple_of_two_empty_tuple_no_arms() { 940 fn tuple_of_two_empty_tuple_no_arms() {
909 let content = r" 941 check_diagnostic(
942 r"
910 fn test_fn() { 943 fn test_fn() {
911 match ((), ()) { 944 match ((), ()) {
912 } 945 }
913 } 946 }
914 "; 947 ",
915 948 );
916 check_diagnostic(content);
917 } 949 }
918 950
919 #[test] 951 #[test]
920 fn tuple_of_two_empty_tuple_no_diagnostic() { 952 fn tuple_of_two_empty_tuple_no_diagnostic() {
921 let content = r" 953 check_no_diagnostic(
954 r"
922 fn test_fn() { 955 fn test_fn() {
923 match ((), ()) { 956 match ((), ()) {
924 ((), ()) => {} 957 ((), ()) => {}
925 } 958 }
926 } 959 }
927 "; 960 ",
928 961 );
929 check_no_diagnostic(content);
930 } 962 }
931 963
932 #[test] 964 #[test]
933 fn bool_no_arms() { 965 fn bool_no_arms() {
934 let content = r" 966 check_diagnostic(
967 r"
935 fn test_fn() { 968 fn test_fn() {
936 match false { 969 match false {
937 } 970 }
938 } 971 }
939 "; 972 ",
940 973 );
941 check_diagnostic(content);
942 } 974 }
943 975
944 #[test] 976 #[test]
945 fn bool_missing_arm() { 977 fn bool_missing_arm() {
946 let content = r" 978 check_diagnostic(
979 r"
947 fn test_fn() { 980 fn test_fn() {
948 match false { 981 match false {
949 true => {} 982 true => {}
950 } 983 }
951 } 984 }
952 "; 985 ",
953 986 );
954 check_diagnostic(content);
955 } 987 }
956 988
957 #[test] 989 #[test]
958 fn bool_no_diagnostic() { 990 fn bool_no_diagnostic() {
959 let content = r" 991 check_no_diagnostic(
992 r"
960 fn test_fn() { 993 fn test_fn() {
961 match false { 994 match false {
962 true => {} 995 true => {}
963 false => {} 996 false => {}
964 } 997 }
965 } 998 }
966 "; 999 ",
967 1000 );
968 check_no_diagnostic(content);
969 } 1001 }
970 1002
971 #[test] 1003 #[test]
972 fn tuple_of_bools_no_arms() { 1004 fn tuple_of_bools_no_arms() {
973 let content = r" 1005 check_diagnostic(
1006 r"
974 fn test_fn() { 1007 fn test_fn() {
975 match (false, true) { 1008 match (false, true) {
976 } 1009 }
977 } 1010 }
978 "; 1011 ",
979 1012 );
980 check_diagnostic(content);
981 } 1013 }
982 1014
983 #[test] 1015 #[test]
984 fn tuple_of_bools_missing_arms() { 1016 fn tuple_of_bools_missing_arms() {
985 let content = r" 1017 check_diagnostic(
1018 r"
986 fn test_fn() { 1019 fn test_fn() {
987 match (false, true) { 1020 match (false, true) {
988 (true, true) => {}, 1021 (true, true) => {},
989 } 1022 }
990 } 1023 }
991 "; 1024 ",
992 1025 );
993 check_diagnostic(content);
994 } 1026 }
995 1027
996 #[test] 1028 #[test]
997 fn tuple_of_bools_missing_arm() { 1029 fn tuple_of_bools_missing_arm() {
998 let content = r" 1030 check_diagnostic(
1031 r"
999 fn test_fn() { 1032 fn test_fn() {
1000 match (false, true) { 1033 match (false, true) {
1001 (false, true) => {}, 1034 (false, true) => {},
@@ -1003,14 +1036,14 @@ mod tests {
1003 (true, false) => {}, 1036 (true, false) => {},
1004 } 1037 }
1005 } 1038 }
1006 "; 1039 ",
1007 1040 );
1008 check_diagnostic(content);
1009 } 1041 }
1010 1042
1011 #[test] 1043 #[test]
1012 fn tuple_of_bools_with_wilds() { 1044 fn tuple_of_bools_with_wilds() {
1013 let content = r" 1045 check_no_diagnostic(
1046 r"
1014 fn test_fn() { 1047 fn test_fn() {
1015 match (false, true) { 1048 match (false, true) {
1016 (false, _) => {}, 1049 (false, _) => {},
@@ -1018,14 +1051,14 @@ mod tests {
1018 (_, true) => {}, 1051 (_, true) => {},
1019 } 1052 }
1020 } 1053 }
1021 "; 1054 ",
1022 1055 );
1023 check_no_diagnostic(content);
1024 } 1056 }
1025 1057
1026 #[test] 1058 #[test]
1027 fn tuple_of_bools_no_diagnostic() { 1059 fn tuple_of_bools_no_diagnostic() {
1028 let content = r" 1060 check_no_diagnostic(
1061 r"
1029 fn test_fn() { 1062 fn test_fn() {
1030 match (false, true) { 1063 match (false, true) {
1031 (true, true) => {}, 1064 (true, true) => {},
@@ -1034,27 +1067,27 @@ mod tests {
1034 (false, false) => {}, 1067 (false, false) => {},
1035 } 1068 }
1036 } 1069 }
1037 "; 1070 ",
1038 1071 );
1039 check_no_diagnostic(content);
1040 } 1072 }
1041 1073
1042 #[test] 1074 #[test]
1043 fn tuple_of_bools_binding_missing_arms() { 1075 fn tuple_of_bools_binding_missing_arms() {
1044 let content = r" 1076 check_diagnostic(
1077 r"
1045 fn test_fn() { 1078 fn test_fn() {
1046 match (false, true) { 1079 match (false, true) {
1047 (true, _x) => {}, 1080 (true, _x) => {},
1048 } 1081 }
1049 } 1082 }
1050 "; 1083 ",
1051 1084 );
1052 check_diagnostic(content);
1053 } 1085 }
1054 1086
1055 #[test] 1087 #[test]
1056 fn tuple_of_bools_binding_no_diagnostic() { 1088 fn tuple_of_bools_binding_no_diagnostic() {
1057 let content = r" 1089 check_no_diagnostic(
1090 r"
1058 fn test_fn() { 1091 fn test_fn() {
1059 match (false, true) { 1092 match (false, true) {
1060 (true, _x) => {}, 1093 (true, _x) => {},
@@ -1062,80 +1095,80 @@ mod tests {
1062 (false, false) => {}, 1095 (false, false) => {},
1063 } 1096 }
1064 } 1097 }
1065 "; 1098 ",
1066 1099 );
1067 check_no_diagnostic(content);
1068 } 1100 }
1069 1101
1070 #[test] 1102 #[test]
1071 fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() { 1103 fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() {
1072 let content = r" 1104 check_no_diagnostic(
1105 r"
1073 fn test_fn() { 1106 fn test_fn() {
1074 match (false, true, false) { 1107 match (false, true, false) {
1075 (false, ..) => {}, 1108 (false, ..) => {},
1076 (true, ..) => {}, 1109 (true, ..) => {},
1077 } 1110 }
1078 } 1111 }
1079 "; 1112 ",
1080 1113 );
1081 check_no_diagnostic(content);
1082 } 1114 }
1083 1115
1084 #[test] 1116 #[test]
1085 fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() { 1117 fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() {
1086 let content = r" 1118 check_no_diagnostic(
1119 r"
1087 fn test_fn() { 1120 fn test_fn() {
1088 match (false, true, false) { 1121 match (false, true, false) {
1089 (.., false) => {}, 1122 (.., false) => {},
1090 (.., true) => {}, 1123 (.., true) => {},
1091 } 1124 }
1092 } 1125 }
1093 "; 1126 ",
1094 1127 );
1095 check_no_diagnostic(content);
1096 } 1128 }
1097 1129
1098 #[test] 1130 #[test]
1099 fn tuple_of_bools_with_ellipsis_no_diagnostic() { 1131 fn tuple_of_bools_with_ellipsis_no_diagnostic() {
1100 let content = r" 1132 check_no_diagnostic(
1133 r"
1101 fn test_fn() { 1134 fn test_fn() {
1102 match (false, true, false) { 1135 match (false, true, false) {
1103 (..) => {}, 1136 (..) => {},
1104 } 1137 }
1105 } 1138 }
1106 "; 1139 ",
1107 1140 );
1108 check_no_diagnostic(content);
1109 } 1141 }
1110 1142
1111 #[test] 1143 #[test]
1112 fn tuple_of_tuple_and_bools_no_arms() { 1144 fn tuple_of_tuple_and_bools_no_arms() {
1113 let content = r" 1145 check_diagnostic(
1146 r"
1114 fn test_fn() { 1147 fn test_fn() {
1115 match (false, ((), false)) { 1148 match (false, ((), false)) {
1116 } 1149 }
1117 } 1150 }
1118 "; 1151 ",
1119 1152 );
1120 check_diagnostic(content);
1121 } 1153 }
1122 1154
1123 #[test] 1155 #[test]
1124 fn tuple_of_tuple_and_bools_missing_arms() { 1156 fn tuple_of_tuple_and_bools_missing_arms() {
1125 let content = r" 1157 check_diagnostic(
1158 r"
1126 fn test_fn() { 1159 fn test_fn() {
1127 match (false, ((), false)) { 1160 match (false, ((), false)) {
1128 (true, ((), true)) => {}, 1161 (true, ((), true)) => {},
1129 } 1162 }
1130 } 1163 }
1131 "; 1164 ",
1132 1165 );
1133 check_diagnostic(content);
1134 } 1166 }
1135 1167
1136 #[test] 1168 #[test]
1137 fn tuple_of_tuple_and_bools_no_diagnostic() { 1169 fn tuple_of_tuple_and_bools_no_diagnostic() {
1138 let content = r" 1170 check_no_diagnostic(
1171 r"
1139 fn test_fn() { 1172 fn test_fn() {
1140 match (false, ((), false)) { 1173 match (false, ((), false)) {
1141 (true, ((), true)) => {}, 1174 (true, ((), true)) => {},
@@ -1144,27 +1177,27 @@ mod tests {
1144 (false, ((), false)) => {}, 1177 (false, ((), false)) => {},
1145 } 1178 }
1146 } 1179 }
1147 "; 1180 ",
1148 1181 );
1149 check_no_diagnostic(content);
1150 } 1182 }
1151 1183
1152 #[test] 1184 #[test]
1153 fn tuple_of_tuple_and_bools_wildcard_missing_arms() { 1185 fn tuple_of_tuple_and_bools_wildcard_missing_arms() {
1154 let content = r" 1186 check_diagnostic(
1187 r"
1155 fn test_fn() { 1188 fn test_fn() {
1156 match (false, ((), false)) { 1189 match (false, ((), false)) {
1157 (true, _) => {}, 1190 (true, _) => {},
1158 } 1191 }
1159 } 1192 }
1160 "; 1193 ",
1161 1194 );
1162 check_diagnostic(content);
1163 } 1195 }
1164 1196
1165 #[test] 1197 #[test]
1166 fn tuple_of_tuple_and_bools_wildcard_no_diagnostic() { 1198 fn tuple_of_tuple_and_bools_wildcard_no_diagnostic() {
1167 let content = r" 1199 check_no_diagnostic(
1200 r"
1168 fn test_fn() { 1201 fn test_fn() {
1169 match (false, ((), false)) { 1202 match (false, ((), false)) {
1170 (true, ((), true)) => {}, 1203 (true, ((), true)) => {},
@@ -1172,14 +1205,14 @@ mod tests {
1172 (false, _) => {}, 1205 (false, _) => {},
1173 } 1206 }
1174 } 1207 }
1175 "; 1208 ",
1176 1209 );
1177 check_no_diagnostic(content);
1178 } 1210 }
1179 1211
1180 #[test] 1212 #[test]
1181 fn enum_no_arms() { 1213 fn enum_no_arms() {
1182 let content = r" 1214 check_diagnostic(
1215 r"
1183 enum Either { 1216 enum Either {
1184 A, 1217 A,
1185 B, 1218 B,
@@ -1188,14 +1221,14 @@ mod tests {
1188 match Either::A { 1221 match Either::A {
1189 } 1222 }
1190 } 1223 }
1191 "; 1224 ",
1192 1225 );
1193 check_diagnostic(content);
1194 } 1226 }
1195 1227
1196 #[test] 1228 #[test]
1197 fn enum_missing_arms() { 1229 fn enum_missing_arms() {
1198 let content = r" 1230 check_diagnostic(
1231 r"
1199 enum Either { 1232 enum Either {
1200 A, 1233 A,
1201 B, 1234 B,
@@ -1205,14 +1238,14 @@ mod tests {
1205 Either::A => {}, 1238 Either::A => {},
1206 } 1239 }
1207 } 1240 }
1208 "; 1241 ",
1209 1242 );
1210 check_diagnostic(content);
1211 } 1243 }
1212 1244
1213 #[test] 1245 #[test]
1214 fn enum_no_diagnostic() { 1246 fn enum_no_diagnostic() {
1215 let content = r" 1247 check_no_diagnostic(
1248 r"
1216 enum Either { 1249 enum Either {
1217 A, 1250 A,
1218 B, 1251 B,
@@ -1223,14 +1256,14 @@ mod tests {
1223 Either::B => {}, 1256 Either::B => {},
1224 } 1257 }
1225 } 1258 }
1226 "; 1259 ",
1227 1260 );
1228 check_no_diagnostic(content);
1229 } 1261 }
1230 1262
1231 #[test] 1263 #[test]
1232 fn enum_ref_missing_arms() { 1264 fn enum_ref_missing_arms() {
1233 let content = r" 1265 check_diagnostic(
1266 r"
1234 enum Either { 1267 enum Either {
1235 A, 1268 A,
1236 B, 1269 B,
@@ -1240,14 +1273,14 @@ mod tests {
1240 Either::A => {}, 1273 Either::A => {},
1241 } 1274 }
1242 } 1275 }
1243 "; 1276 ",
1244 1277 );
1245 check_diagnostic(content);
1246 } 1278 }
1247 1279
1248 #[test] 1280 #[test]
1249 fn enum_ref_no_diagnostic() { 1281 fn enum_ref_no_diagnostic() {
1250 let content = r" 1282 check_no_diagnostic(
1283 r"
1251 enum Either { 1284 enum Either {
1252 A, 1285 A,
1253 B, 1286 B,
@@ -1258,14 +1291,14 @@ mod tests {
1258 Either::B => {}, 1291 Either::B => {},
1259 } 1292 }
1260 } 1293 }
1261 "; 1294 ",
1262 1295 );
1263 check_no_diagnostic(content);
1264 } 1296 }
1265 1297
1266 #[test] 1298 #[test]
1267 fn enum_containing_bool_no_arms() { 1299 fn enum_containing_bool_no_arms() {
1268 let content = r" 1300 check_diagnostic(
1301 r"
1269 enum Either { 1302 enum Either {
1270 A(bool), 1303 A(bool),
1271 B, 1304 B,
@@ -1274,14 +1307,14 @@ mod tests {
1274 match Either::B { 1307 match Either::B {
1275 } 1308 }
1276 } 1309 }
1277 "; 1310 ",
1278 1311 );
1279 check_diagnostic(content);
1280 } 1312 }
1281 1313
1282 #[test] 1314 #[test]
1283 fn enum_containing_bool_missing_arms() { 1315 fn enum_containing_bool_missing_arms() {
1284 let content = r" 1316 check_diagnostic(
1317 r"
1285 enum Either { 1318 enum Either {
1286 A(bool), 1319 A(bool),
1287 B, 1320 B,
@@ -1292,14 +1325,14 @@ mod tests {
1292 Either::B => (), 1325 Either::B => (),
1293 } 1326 }
1294 } 1327 }
1295 "; 1328 ",
1296 1329 );
1297 check_diagnostic(content);
1298 } 1330 }
1299 1331
1300 #[test] 1332 #[test]
1301 fn enum_containing_bool_no_diagnostic() { 1333 fn enum_containing_bool_no_diagnostic() {
1302 let content = r" 1334 check_no_diagnostic(
1335 r"
1303 enum Either { 1336 enum Either {
1304 A(bool), 1337 A(bool),
1305 B, 1338 B,
@@ -1311,14 +1344,14 @@ mod tests {
1311 Either::B => (), 1344 Either::B => (),
1312 } 1345 }
1313 } 1346 }
1314 "; 1347 ",
1315 1348 );
1316 check_no_diagnostic(content);
1317 } 1349 }
1318 1350
1319 #[test] 1351 #[test]
1320 fn enum_containing_bool_with_wild_no_diagnostic() { 1352 fn enum_containing_bool_with_wild_no_diagnostic() {
1321 let content = r" 1353 check_no_diagnostic(
1354 r"
1322 enum Either { 1355 enum Either {
1323 A(bool), 1356 A(bool),
1324 B, 1357 B,
@@ -1329,14 +1362,14 @@ mod tests {
1329 _ => (), 1362 _ => (),
1330 } 1363 }
1331 } 1364 }
1332 "; 1365 ",
1333 1366 );
1334 check_no_diagnostic(content);
1335 } 1367 }
1336 1368
1337 #[test] 1369 #[test]
1338 fn enum_containing_bool_with_wild_2_no_diagnostic() { 1370 fn enum_containing_bool_with_wild_2_no_diagnostic() {
1339 let content = r" 1371 check_no_diagnostic(
1372 r"
1340 enum Either { 1373 enum Either {
1341 A(bool), 1374 A(bool),
1342 B, 1375 B,
@@ -1347,14 +1380,14 @@ mod tests {
1347 Either::B => (), 1380 Either::B => (),
1348 } 1381 }
1349 } 1382 }
1350 "; 1383 ",
1351 1384 );
1352 check_no_diagnostic(content);
1353 } 1385 }
1354 1386
1355 #[test] 1387 #[test]
1356 fn enum_different_sizes_missing_arms() { 1388 fn enum_different_sizes_missing_arms() {
1357 let content = r" 1389 check_diagnostic(
1390 r"
1358 enum Either { 1391 enum Either {
1359 A(bool), 1392 A(bool),
1360 B(bool, bool), 1393 B(bool, bool),
@@ -1365,14 +1398,14 @@ mod tests {
1365 Either::B(false, _) => (), 1398 Either::B(false, _) => (),
1366 } 1399 }
1367 } 1400 }
1368 "; 1401 ",
1369 1402 );
1370 check_diagnostic(content);
1371 } 1403 }
1372 1404
1373 #[test] 1405 #[test]
1374 fn enum_different_sizes_no_diagnostic() { 1406 fn enum_different_sizes_no_diagnostic() {
1375 let content = r" 1407 check_no_diagnostic(
1408 r"
1376 enum Either { 1409 enum Either {
1377 A(bool), 1410 A(bool),
1378 B(bool, bool), 1411 B(bool, bool),
@@ -1384,14 +1417,14 @@ mod tests {
1384 Either::B(false, _) => (), 1417 Either::B(false, _) => (),
1385 } 1418 }
1386 } 1419 }
1387 "; 1420 ",
1388 1421 );
1389 check_no_diagnostic(content);
1390 } 1422 }
1391 1423
1392 #[test] 1424 #[test]
1393 fn or_no_diagnostic() { 1425 fn or_no_diagnostic() {
1394 let content = r" 1426 check_no_diagnostic(
1427 r"
1395 enum Either { 1428 enum Either {
1396 A(bool), 1429 A(bool),
1397 B(bool, bool), 1430 B(bool, bool),
@@ -1403,14 +1436,14 @@ mod tests {
1403 Either::B(false, _) => (), 1436 Either::B(false, _) => (),
1404 } 1437 }
1405 } 1438 }
1406 "; 1439 ",
1407 1440 );
1408 check_no_diagnostic(content);
1409 } 1441 }
1410 1442
1411 #[test] 1443 #[test]
1412 fn tuple_of_enum_no_diagnostic() { 1444 fn tuple_of_enum_no_diagnostic() {
1413 let content = r" 1445 check_no_diagnostic(
1446 r"
1414 enum Either { 1447 enum Either {
1415 A(bool), 1448 A(bool),
1416 B(bool, bool), 1449 B(bool, bool),
@@ -1427,14 +1460,16 @@ mod tests {
1427 (Either::B(_, _), Either2::D) => (), 1460 (Either::B(_, _), Either2::D) => (),
1428 } 1461 }
1429 } 1462 }
1430 "; 1463 ",
1431 1464 );
1432 check_no_diagnostic(content);
1433 } 1465 }
1434 1466
1435 #[test] 1467 #[test]
1436 fn mismatched_types() { 1468 fn mismatched_types() {
1437 let content = r" 1469 // Match statements with arms that don't match the
1470 // expression pattern do not fire this diagnostic.
1471 check_no_diagnostic(
1472 r"
1438 enum Either { 1473 enum Either {
1439 A, 1474 A,
1440 B, 1475 B,
@@ -1449,47 +1484,47 @@ mod tests {
1449 Either2::D => (), 1484 Either2::D => (),
1450 } 1485 }
1451 } 1486 }
1452 "; 1487 ",
1453 1488 );
1454 // Match statements with arms that don't match the
1455 // expression pattern do not fire this diagnostic.
1456 check_no_diagnostic(content);
1457 } 1489 }
1458 1490
1459 #[test] 1491 #[test]
1460 fn mismatched_types_with_different_arity() { 1492 fn mismatched_types_with_different_arity() {
1461 let content = r" 1493 // Match statements with arms that don't match the
1494 // expression pattern do not fire this diagnostic.
1495 check_no_diagnostic(
1496 r"
1462 fn test_fn() { 1497 fn test_fn() {
1463 match (true, false) { 1498 match (true, false) {
1464 (true, false, true) => (), 1499 (true, false, true) => (),
1465 (true) => (), 1500 (true) => (),
1466 } 1501 }
1467 } 1502 }
1468 "; 1503 ",
1469 1504 );
1470 // Match statements with arms that don't match the
1471 // expression pattern do not fire this diagnostic.
1472 check_no_diagnostic(content);
1473 } 1505 }
1474 1506
1475 #[test] 1507 #[test]
1476 fn malformed_match_arm_tuple_missing_pattern() { 1508 fn malformed_match_arm_tuple_missing_pattern() {
1477 let content = r" 1509 // Match statements with arms that don't match the
1510 // expression pattern do not fire this diagnostic.
1511 check_no_diagnostic(
1512 r"
1478 fn test_fn() { 1513 fn test_fn() {
1479 match (0) { 1514 match (0) {
1480 () => (), 1515 () => (),
1481 } 1516 }
1482 } 1517 }
1483 "; 1518 ",
1484 1519 );
1485 // Match statements with arms that don't match the
1486 // expression pattern do not fire this diagnostic.
1487 check_no_diagnostic(content);
1488 } 1520 }
1489 1521
1490 #[test] 1522 #[test]
1491 fn malformed_match_arm_tuple_enum_missing_pattern() { 1523 fn malformed_match_arm_tuple_enum_missing_pattern() {
1492 let content = r" 1524 // We are testing to be sure we don't panic here when the match
1525 // arm `Either::B` is missing its pattern.
1526 check_no_diagnostic(
1527 r"
1493 enum Either { 1528 enum Either {
1494 A, 1529 A,
1495 B(u32), 1530 B(u32),
@@ -1500,32 +1535,30 @@ mod tests {
1500 Either::B() => (), 1535 Either::B() => (),
1501 } 1536 }
1502 } 1537 }
1503 "; 1538 ",
1504 1539 );
1505 // We are testing to be sure we don't panic here when the match
1506 // arm `Either::B` is missing its pattern.
1507 check_no_diagnostic(content);
1508 } 1540 }
1509 1541
1510 #[test] 1542 #[test]
1511 fn enum_not_in_scope() { 1543 fn enum_not_in_scope() {
1512 let content = r" 1544 // The enum is not in scope so we don't perform exhaustiveness
1545 // checking, but we want to be sure we don't panic here (and
1546 // we don't create a diagnostic).
1547 check_no_diagnostic(
1548 r"
1513 fn test_fn() { 1549 fn test_fn() {
1514 match Foo::Bar { 1550 match Foo::Bar {
1515 Foo::Baz => (), 1551 Foo::Baz => (),
1516 } 1552 }
1517 } 1553 }
1518 "; 1554 ",
1519 1555 );
1520 // The enum is not in scope so we don't perform exhaustiveness
1521 // checking, but we want to be sure we don't panic here (and
1522 // we don't create a diagnostic).
1523 check_no_diagnostic(content);
1524 } 1556 }
1525 1557
1526 #[test] 1558 #[test]
1527 fn expr_diverges() { 1559 fn expr_diverges() {
1528 let content = r" 1560 check_no_diagnostic(
1561 r"
1529 enum Either { 1562 enum Either {
1530 A, 1563 A,
1531 B, 1564 B,
@@ -1536,14 +1569,14 @@ mod tests {
1536 Either::B => (), 1569 Either::B => (),
1537 } 1570 }
1538 } 1571 }
1539 "; 1572 ",
1540 1573 );
1541 check_no_diagnostic(content);
1542 } 1574 }
1543 1575
1544 #[test] 1576 #[test]
1545 fn expr_loop_with_break() { 1577 fn expr_loop_with_break() {
1546 let content = r" 1578 check_no_diagnostic(
1579 r"
1547 enum Either { 1580 enum Either {
1548 A, 1581 A,
1549 B, 1582 B,
@@ -1554,14 +1587,14 @@ mod tests {
1554 Either::B => (), 1587 Either::B => (),
1555 } 1588 }
1556 } 1589 }
1557 "; 1590 ",
1558 1591 );
1559 check_no_diagnostic(content);
1560 } 1592 }
1561 1593
1562 #[test] 1594 #[test]
1563 fn expr_partially_diverges() { 1595 fn expr_partially_diverges() {
1564 let content = r" 1596 check_no_diagnostic(
1597 r"
1565 enum Either<T> { 1598 enum Either<T> {
1566 A(T), 1599 A(T),
1567 B, 1600 B,
@@ -1575,14 +1608,14 @@ mod tests {
1575 Either::B => 0, 1608 Either::B => 0,
1576 } 1609 }
1577 } 1610 }
1578 "; 1611 ",
1579 1612 );
1580 check_no_diagnostic(content);
1581 } 1613 }
1582 1614
1583 #[test] 1615 #[test]
1584 fn enum_record_no_arms() { 1616 fn enum_record_no_arms() {
1585 let content = r" 1617 check_diagnostic(
1618 r"
1586 enum Either { 1619 enum Either {
1587 A { foo: bool }, 1620 A { foo: bool },
1588 B, 1621 B,
@@ -1592,14 +1625,14 @@ mod tests {
1592 match a { 1625 match a {
1593 } 1626 }
1594 } 1627 }
1595 "; 1628 ",
1596 1629 );
1597 check_diagnostic(content);
1598 } 1630 }
1599 1631
1600 #[test] 1632 #[test]
1601 fn enum_record_missing_arms() { 1633 fn enum_record_missing_arms() {
1602 let content = r" 1634 check_diagnostic(
1635 r"
1603 enum Either { 1636 enum Either {
1604 A { foo: bool }, 1637 A { foo: bool },
1605 B, 1638 B,
@@ -1610,14 +1643,14 @@ mod tests {
1610 Either::A { foo: true } => (), 1643 Either::A { foo: true } => (),
1611 } 1644 }
1612 } 1645 }
1613 "; 1646 ",
1614 1647 );
1615 check_diagnostic(content);
1616 } 1648 }
1617 1649
1618 #[test] 1650 #[test]
1619 fn enum_record_no_diagnostic() { 1651 fn enum_record_no_diagnostic() {
1620 let content = r" 1652 check_no_diagnostic(
1653 r"
1621 enum Either { 1654 enum Either {
1622 A { foo: bool }, 1655 A { foo: bool },
1623 B, 1656 B,
@@ -1630,14 +1663,17 @@ mod tests {
1630 Either::B => (), 1663 Either::B => (),
1631 } 1664 }
1632 } 1665 }
1633 "; 1666 ",
1634 1667 );
1635 check_no_diagnostic(content);
1636 } 1668 }
1637 1669
1638 #[test] 1670 #[test]
1639 fn enum_record_missing_field_no_diagnostic() { 1671 fn enum_record_missing_field_no_diagnostic() {
1640 let content = r" 1672 // When `Either::A` is missing a struct member, we don't want
1673 // to fire the missing match arm diagnostic. This should fire
1674 // some other diagnostic.
1675 check_no_diagnostic(
1676 r"
1641 enum Either { 1677 enum Either {
1642 A { foo: bool }, 1678 A { foo: bool },
1643 B, 1679 B,
@@ -1649,17 +1685,16 @@ mod tests {
1649 Either::B => (), 1685 Either::B => (),
1650 } 1686 }
1651 } 1687 }
1652 "; 1688 ",
1653 1689 );
1654 // When `Either::A` is missing a struct member, we don't want
1655 // to fire the missing match arm diagnostic. This should fire
1656 // some other diagnostic.
1657 check_no_diagnostic(content);
1658 } 1690 }
1659 1691
1660 #[test] 1692 #[test]
1661 fn enum_record_missing_field_missing_match_arm() { 1693 fn enum_record_missing_field_missing_match_arm() {
1662 let content = r" 1694 // Even though `Either::A` is missing fields, we still want to fire
1695 // the missing arm diagnostic here, since we know `Either::B` is missing.
1696 check_diagnostic(
1697 r"
1663 enum Either { 1698 enum Either {
1664 A { foo: bool }, 1699 A { foo: bool },
1665 B, 1700 B,
@@ -1670,16 +1705,14 @@ mod tests {
1670 Either::A { } => (), 1705 Either::A { } => (),
1671 } 1706 }
1672 } 1707 }
1673 "; 1708 ",
1674 1709 );
1675 // Even though `Either::A` is missing fields, we still want to fire
1676 // the missing arm diagnostic here, since we know `Either::B` is missing.
1677 check_diagnostic(content);
1678 } 1710 }
1679 1711
1680 #[test] 1712 #[test]
1681 fn enum_record_no_diagnostic_wild() { 1713 fn enum_record_no_diagnostic_wild() {
1682 let content = r" 1714 check_no_diagnostic(
1715 r"
1683 enum Either { 1716 enum Either {
1684 A { foo: bool }, 1717 A { foo: bool },
1685 B, 1718 B,
@@ -1691,14 +1724,14 @@ mod tests {
1691 Either::B => (), 1724 Either::B => (),
1692 } 1725 }
1693 } 1726 }
1694 "; 1727 ",
1695 1728 );
1696 check_no_diagnostic(content);
1697 } 1729 }
1698 1730
1699 #[test] 1731 #[test]
1700 fn enum_record_fields_out_of_order_missing_arm() { 1732 fn enum_record_fields_out_of_order_missing_arm() {
1701 let content = r" 1733 check_diagnostic(
1734 r"
1702 enum Either { 1735 enum Either {
1703 A { foo: bool, bar: () }, 1736 A { foo: bool, bar: () },
1704 B, 1737 B,
@@ -1710,14 +1743,14 @@ mod tests {
1710 Either::A { foo: true, bar: () } => (), 1743 Either::A { foo: true, bar: () } => (),
1711 } 1744 }
1712 } 1745 }
1713 "; 1746 ",
1714 1747 );
1715 check_diagnostic(content);
1716 } 1748 }
1717 1749
1718 #[test] 1750 #[test]
1719 fn enum_record_fields_out_of_order_no_diagnostic() { 1751 fn enum_record_fields_out_of_order_no_diagnostic() {
1720 let content = r" 1752 check_no_diagnostic(
1753 r"
1721 enum Either { 1754 enum Either {
1722 A { foo: bool, bar: () }, 1755 A { foo: bool, bar: () },
1723 B, 1756 B,
@@ -1730,89 +1763,89 @@ mod tests {
1730 Either::B => (), 1763 Either::B => (),
1731 } 1764 }
1732 } 1765 }
1733 "; 1766 ",
1734 1767 );
1735 check_no_diagnostic(content);
1736 } 1768 }
1737 1769
1738 #[test] 1770 #[test]
1739 fn enum_record_ellipsis_missing_arm() { 1771 fn enum_record_ellipsis_missing_arm() {
1740 let content = r" 1772 check_diagnostic(
1741 enum Either { 1773 r"
1742 A { foo: bool, bar: bool }, 1774 enum Either {
1743 B, 1775 A { foo: bool, bar: bool },
1744 } 1776 B,
1745 fn test_fn() { 1777 }
1746 match Either::B { 1778 fn test_fn() {
1747 Either::A { foo: true, .. } => (), 1779 match Either::B {
1748 Either::B => (), 1780 Either::A { foo: true, .. } => (),
1749 } 1781 Either::B => (),
1750 } 1782 }
1751 "; 1783 }
1752 1784 ",
1753 check_diagnostic(content); 1785 );
1754 } 1786 }
1755 1787
1756 #[test] 1788 #[test]
1757 fn enum_record_ellipsis_no_diagnostic() { 1789 fn enum_record_ellipsis_no_diagnostic() {
1758 let content = r" 1790 check_no_diagnostic(
1759 enum Either { 1791 r"
1760 A { foo: bool, bar: bool }, 1792 enum Either {
1761 B, 1793 A { foo: bool, bar: bool },
1762 } 1794 B,
1763 fn test_fn() { 1795 }
1764 let a = Either::A { foo: true }; 1796 fn test_fn() {
1765 match a { 1797 let a = Either::A { foo: true };
1766 Either::A { foo: true, .. } => (), 1798 match a {
1767 Either::A { foo: false, .. } => (), 1799 Either::A { foo: true, .. } => (),
1768 Either::B => (), 1800 Either::A { foo: false, .. } => (),
1769 } 1801 Either::B => (),
1770 } 1802 }
1771 "; 1803 }
1772 1804 ",
1773 check_no_diagnostic(content); 1805 );
1774 } 1806 }
1775 1807
1776 #[test] 1808 #[test]
1777 fn enum_record_ellipsis_all_fields_missing_arm() { 1809 fn enum_record_ellipsis_all_fields_missing_arm() {
1778 let content = r" 1810 check_diagnostic(
1779 enum Either { 1811 r"
1780 A { foo: bool, bar: bool }, 1812 enum Either {
1781 B, 1813 A { foo: bool, bar: bool },
1782 } 1814 B,
1783 fn test_fn() { 1815 }
1784 let a = Either::B; 1816 fn test_fn() {
1785 match a { 1817 let a = Either::B;
1786 Either::A { .. } => (), 1818 match a {
1787 } 1819 Either::A { .. } => (),
1788 } 1820 }
1789 "; 1821 }
1790 1822 ",
1791 check_diagnostic(content); 1823 );
1792 } 1824 }
1793 1825
1794 #[test] 1826 #[test]
1795 fn enum_record_ellipsis_all_fields_no_diagnostic() { 1827 fn enum_record_ellipsis_all_fields_no_diagnostic() {
1796 let content = r" 1828 check_no_diagnostic(
1797 enum Either { 1829 r"
1798 A { foo: bool, bar: bool }, 1830 enum Either {
1799 B, 1831 A { foo: bool, bar: bool },
1800 } 1832 B,
1801 fn test_fn() { 1833 }
1802 let a = Either::B; 1834 fn test_fn() {
1803 match a { 1835 let a = Either::B;
1804 Either::A { .. } => (), 1836 match a {
1805 Either::B => (), 1837 Either::A { .. } => (),
1806 } 1838 Either::B => (),
1807 } 1839 }
1808 "; 1840 }
1809 1841 ",
1810 check_no_diagnostic(content); 1842 );
1811 } 1843 }
1812 1844
1813 #[test] 1845 #[test]
1814 fn enum_tuple_partial_ellipsis_no_diagnostic() { 1846 fn enum_tuple_partial_ellipsis_no_diagnostic() {
1815 let content = r" 1847 check_no_diagnostic(
1848 r"
1816 enum Either { 1849 enum Either {
1817 A(bool, bool, bool, bool), 1850 A(bool, bool, bool, bool),
1818 B, 1851 B,
@@ -1826,14 +1859,14 @@ mod tests {
1826 Either::B => {}, 1859 Either::B => {},
1827 } 1860 }
1828 } 1861 }
1829 "; 1862 ",
1830 1863 );
1831 check_no_diagnostic(content);
1832 } 1864 }
1833 1865
1834 #[test] 1866 #[test]
1835 fn enum_tuple_partial_ellipsis_2_no_diagnostic() { 1867 fn enum_tuple_partial_ellipsis_2_no_diagnostic() {
1836 let content = r" 1868 check_no_diagnostic(
1869 r"
1837 enum Either { 1870 enum Either {
1838 A(bool, bool, bool, bool), 1871 A(bool, bool, bool, bool),
1839 B, 1872 B,
@@ -1847,14 +1880,14 @@ mod tests {
1847 Either::B => {}, 1880 Either::B => {},
1848 } 1881 }
1849 } 1882 }
1850 "; 1883 ",
1851 1884 );
1852 check_no_diagnostic(content);
1853 } 1885 }
1854 1886
1855 #[test] 1887 #[test]
1856 fn enum_tuple_partial_ellipsis_missing_arm() { 1888 fn enum_tuple_partial_ellipsis_missing_arm() {
1857 let content = r" 1889 check_diagnostic(
1890 r"
1858 enum Either { 1891 enum Either {
1859 A(bool, bool, bool, bool), 1892 A(bool, bool, bool, bool),
1860 B, 1893 B,
@@ -1867,14 +1900,14 @@ mod tests {
1867 Either::B => {}, 1900 Either::B => {},
1868 } 1901 }
1869 } 1902 }
1870 "; 1903 ",
1871 1904 );
1872 check_diagnostic(content);
1873 } 1905 }
1874 1906
1875 #[test] 1907 #[test]
1876 fn enum_tuple_partial_ellipsis_2_missing_arm() { 1908 fn enum_tuple_partial_ellipsis_2_missing_arm() {
1877 let content = r" 1909 check_diagnostic(
1910 r"
1878 enum Either { 1911 enum Either {
1879 A(bool, bool, bool, bool), 1912 A(bool, bool, bool, bool),
1880 B, 1913 B,
@@ -1887,14 +1920,14 @@ mod tests {
1887 Either::B => {}, 1920 Either::B => {},
1888 } 1921 }
1889 } 1922 }
1890 "; 1923 ",
1891 1924 );
1892 check_diagnostic(content);
1893 } 1925 }
1894 1926
1895 #[test] 1927 #[test]
1896 fn enum_tuple_ellipsis_no_diagnostic() { 1928 fn enum_tuple_ellipsis_no_diagnostic() {
1897 let content = r" 1929 check_no_diagnostic(
1930 r"
1898 enum Either { 1931 enum Either {
1899 A(bool, bool, bool, bool), 1932 A(bool, bool, bool, bool),
1900 B, 1933 B,
@@ -1905,51 +1938,51 @@ mod tests {
1905 Either::B => {}, 1938 Either::B => {},
1906 } 1939 }
1907 } 1940 }
1908 "; 1941 ",
1909 1942 );
1910 check_no_diagnostic(content);
1911 } 1943 }
1912 1944
1913 #[test] 1945 #[test]
1914 fn enum_never() { 1946 fn enum_never() {
1915 let content = r" 1947 check_no_diagnostic(
1948 r"
1916 enum Never {} 1949 enum Never {}
1917 1950
1918 fn test_fn(never: Never) { 1951 fn test_fn(never: Never) {
1919 match never {} 1952 match never {}
1920 } 1953 }
1921 "; 1954 ",
1922 1955 );
1923 check_no_diagnostic(content);
1924 } 1956 }
1925 1957
1926 #[test] 1958 #[test]
1927 fn type_never() { 1959 fn type_never() {
1928 let content = r" 1960 check_no_diagnostic(
1961 r"
1929 fn test_fn(never: !) { 1962 fn test_fn(never: !) {
1930 match never {} 1963 match never {}
1931 } 1964 }
1932 "; 1965 ",
1933 1966 );
1934 check_no_diagnostic(content);
1935 } 1967 }
1936 1968
1937 #[test] 1969 #[test]
1938 fn enum_never_ref() { 1970 fn enum_never_ref() {
1939 let content = r" 1971 check_no_diagnostic(
1972 r"
1940 enum Never {} 1973 enum Never {}
1941 1974
1942 fn test_fn(never: &Never) { 1975 fn test_fn(never: &Never) {
1943 match never {} 1976 match never {}
1944 } 1977 }
1945 "; 1978 ",
1946 1979 );
1947 check_no_diagnostic(content);
1948 } 1980 }
1949 1981
1950 #[test] 1982 #[test]
1951 fn expr_diverges_missing_arm() { 1983 fn expr_diverges_missing_arm() {
1952 let content = r" 1984 check_no_diagnostic(
1985 r"
1953 enum Either { 1986 enum Either {
1954 A, 1987 A,
1955 B, 1988 B,
@@ -1959,9 +1992,27 @@ mod tests {
1959 Either::A => (), 1992 Either::A => (),
1960 } 1993 }
1961 } 1994 }
1962 "; 1995 ",
1996 );
1997 }
1963 1998
1964 check_no_diagnostic(content); 1999 #[test]
2000 fn or_pattern_panic() {
2001 check_no_diagnostic(
2002 r"
2003 pub enum Category {
2004 Infinity,
2005 Zero,
2006 }
2007
2008 fn panic(a: Category, b: Category) {
2009 match (a, b) {
2010 (Category::Zero | Category::Infinity, _) => {}
2011 (_, Category::Zero | Category::Infinity) => {}
2012 }
2013 }
2014 ",
2015 );
1965 } 2016 }
1966} 2017}
1967 2018
@@ -1981,23 +2032,26 @@ mod false_negatives {
1981 2032
1982 #[test] 2033 #[test]
1983 fn integers() { 2034 fn integers() {
1984 let content = r" 2035 // This is a false negative.
2036 // We don't currently check integer exhaustiveness.
2037 check_no_diagnostic(
2038 r"
1985 fn test_fn() { 2039 fn test_fn() {
1986 match 5 { 2040 match 5 {
1987 10 => (), 2041 10 => (),
1988 11..20 => (), 2042 11..20 => (),
1989 } 2043 }
1990 } 2044 }
1991 "; 2045 ",
1992 2046 );
1993 // This is a false negative.
1994 // We don't currently check integer exhaustiveness.
1995 check_no_diagnostic(content);
1996 } 2047 }
1997 2048
1998 #[test] 2049 #[test]
1999 fn internal_or() { 2050 fn internal_or() {
2000 let content = r" 2051 // This is a false negative.
2052 // We do not currently handle patterns with internal `or`s.
2053 check_no_diagnostic(
2054 r"
2001 fn test_fn() { 2055 fn test_fn() {
2002 enum Either { 2056 enum Either {
2003 A(bool), 2057 A(bool),
@@ -2007,16 +2061,18 @@ mod false_negatives {
2007 Either::A(true | false) => (), 2061 Either::A(true | false) => (),
2008 } 2062 }
2009 } 2063 }
2010 "; 2064 ",
2011 2065 );
2012 // This is a false negative.
2013 // We do not currently handle patterns with internal `or`s.
2014 check_no_diagnostic(content);
2015 } 2066 }
2016 2067
2017 #[test] 2068 #[test]
2018 fn expr_loop_missing_arm() { 2069 fn expr_loop_missing_arm() {
2019 let content = r" 2070 // This is a false negative.
2071 // We currently infer the type of `loop { break Foo::A }` to `!`, which
2072 // causes us to skip the diagnostic since `Either::A` doesn't type check
2073 // with `!`.
2074 check_diagnostic(
2075 r"
2020 enum Either { 2076 enum Either {
2021 A, 2077 A,
2022 B, 2078 B,
@@ -2026,48 +2082,46 @@ mod false_negatives {
2026 Either::A => (), 2082 Either::A => (),
2027 } 2083 }
2028 } 2084 }
2029 "; 2085 ",
2030 2086 );
2031 // This is a false negative.
2032 // We currently infer the type of `loop { break Foo::A }` to `!`, which
2033 // causes us to skip the diagnostic since `Either::A` doesn't type check
2034 // with `!`.
2035 check_diagnostic(content);
2036 } 2087 }
2037 2088
2038 #[test] 2089 #[test]
2039 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { 2090 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
2040 let content = r" 2091 // This is a false negative.
2092 // We don't currently handle tuple patterns with ellipsis.
2093 check_no_diagnostic(
2094 r"
2041 fn test_fn() { 2095 fn test_fn() {
2042 match (false, true, false) { 2096 match (false, true, false) {
2043 (false, ..) => {}, 2097 (false, ..) => {},
2044 } 2098 }
2045 } 2099 }
2046 "; 2100 ",
2047 2101 );
2048 // This is a false negative.
2049 // We don't currently handle tuple patterns with ellipsis.
2050 check_no_diagnostic(content);
2051 } 2102 }
2052 2103
2053 #[test] 2104 #[test]
2054 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { 2105 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
2055 let content = r" 2106 // This is a false negative.
2107 // We don't currently handle tuple patterns with ellipsis.
2108 check_no_diagnostic(
2109 r"
2056 fn test_fn() { 2110 fn test_fn() {
2057 match (false, true, false) { 2111 match (false, true, false) {
2058 (.., false) => {}, 2112 (.., false) => {},
2059 } 2113 }
2060 } 2114 }
2061 "; 2115 ",
2062 2116 );
2063 // This is a false negative.
2064 // We don't currently handle tuple patterns with ellipsis.
2065 check_no_diagnostic(content);
2066 } 2117 }
2067 2118
2068 #[test] 2119 #[test]
2069 fn struct_missing_arm() { 2120 fn struct_missing_arm() {
2070 let content = r" 2121 // This is a false negative.
2122 // We don't currently handle structs.
2123 check_no_diagnostic(
2124 r"
2071 struct Foo { 2125 struct Foo {
2072 a: bool, 2126 a: bool,
2073 } 2127 }
@@ -2076,10 +2130,7 @@ mod false_negatives {
2076 Foo { a: true } => {}, 2130 Foo { a: true } => {},
2077 } 2131 }
2078 } 2132 }
2079 "; 2133 ",
2080 2134 );
2081 // This is a false negative.
2082 // We don't currently handle structs.
2083 check_no_diagnostic(content);
2084 } 2135 }
2085} 2136}
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index a721e23c6..e1fcf379d 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -15,6 +15,7 @@ mod complete_unqualified_path;
15mod complete_postfix; 15mod complete_postfix;
16mod complete_macro_in_item_position; 16mod complete_macro_in_item_position;
17mod complete_trait_impl; 17mod complete_trait_impl;
18mod patterns;
18#[cfg(test)] 19#[cfg(test)]
19mod test_utils; 20mod test_utils;
20 21
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index fd95bc410..b2f621a11 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -1,11 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_syntax::{ 3use ra_syntax::ast;
4 ast::{self, LoopBodyOwner},
5 match_ast, AstNode,
6 SyntaxKind::*,
7 SyntaxToken,
8};
9 4
10use crate::completion::{ 5use crate::completion::{
11 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 6 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
@@ -41,68 +36,122 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
41 } 36 }
42} 37}
43 38
44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { 39pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
45 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) 40 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
46 .kind(CompletionItemKind::Keyword); 41 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
47 42 add_keyword(ctx, acc, "where", "where ");
48 match ctx.config.snippet_cap { 43 return;
49 Some(cap) => res.insert_snippet(cap, snippet),
50 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
51 } 44 }
52 .build() 45 if ctx.unsafe_is_prev {
53} 46 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
47 add_keyword(ctx, acc, "fn", "fn $0() {}")
48 }
49
50 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
51 || ctx.block_expr_parent
52 {
53 add_keyword(ctx, acc, "trait", "trait $0 {}");
54 add_keyword(ctx, acc, "impl", "impl $0 {}");
55 }
54 56
55pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
56 if !ctx.is_trivial_path {
57 return; 57 return;
58 } 58 }
59 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
60 add_keyword(ctx, acc, "fn", "fn $0() {}");
61 }
62 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
63 || ctx.block_expr_parent
64 {
65 add_keyword(ctx, acc, "use", "use ");
66 add_keyword(ctx, acc, "impl", "impl $0 {}");
67 add_keyword(ctx, acc, "trait", "trait $0 {}");
68 }
59 69
60 let fn_def = match &ctx.function_syntax { 70 if ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent {
61 Some(it) => it, 71 add_keyword(ctx, acc, "enum", "enum $0 {}");
62 None => return, 72 add_keyword(ctx, acc, "struct", "struct $0 {}");
63 }; 73 add_keyword(ctx, acc, "union", "union $0 {}");
64 acc.add(keyword(ctx, "if", "if $0 {}")); 74 }
65 acc.add(keyword(ctx, "match", "match $0 {}"));
66 acc.add(keyword(ctx, "while", "while $0 {}"));
67 acc.add(keyword(ctx, "loop", "loop {$0}"));
68 75
76 if ctx.block_expr_parent || ctx.is_match_arm {
77 add_keyword(ctx, acc, "match", "match $0 {}");
78 add_keyword(ctx, acc, "loop", "loop {$0}");
79 }
80 if ctx.block_expr_parent {
81 add_keyword(ctx, acc, "while", "while $0 {}");
82 }
83 if ctx.if_is_prev || ctx.block_expr_parent {
84 add_keyword(ctx, acc, "let", "let ");
85 }
86 if ctx.if_is_prev || ctx.block_expr_parent || ctx.is_match_arm {
87 add_keyword(ctx, acc, "if", "if ");
88 add_keyword(ctx, acc, "if let", "if let ");
89 }
69 if ctx.after_if { 90 if ctx.after_if {
70 acc.add(keyword(ctx, "else", "else {$0}")); 91 add_keyword(ctx, acc, "else", "else {$0}");
71 acc.add(keyword(ctx, "else if", "else if $0 {}")); 92 add_keyword(ctx, acc, "else if", "else if $0 {}");
93 }
94 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
95 || ctx.block_expr_parent
96 {
97 add_keyword(ctx, acc, "mod", "mod $0 {}");
98 }
99 if ctx.bind_pat_parent || ctx.ref_pat_parent {
100 add_keyword(ctx, acc, "mut", "mut ");
72 } 101 }
73 if is_in_loop_body(&ctx.token) { 102 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
103 add_keyword(ctx, acc, "const", "const ");
104 add_keyword(ctx, acc, "type", "type ");
105 }
106 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
107 || ctx.block_expr_parent
108 {
109 add_keyword(ctx, acc, "static", "static ");
110 };
111 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
112 || ctx.block_expr_parent
113 {
114 add_keyword(ctx, acc, "extern", "extern ");
115 }
116 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent || ctx.is_match_arm {
117 add_keyword(ctx, acc, "unsafe", "unsafe ");
118 }
119 if ctx.in_loop_body {
74 if ctx.can_be_stmt { 120 if ctx.can_be_stmt {
75 acc.add(keyword(ctx, "continue", "continue;")); 121 add_keyword(ctx, acc, "continue", "continue;");
76 acc.add(keyword(ctx, "break", "break;")); 122 add_keyword(ctx, acc, "break", "break;");
77 } else { 123 } else {
78 acc.add(keyword(ctx, "continue", "continue")); 124 add_keyword(ctx, acc, "continue", "continue");
79 acc.add(keyword(ctx, "break", "break")); 125 add_keyword(ctx, acc, "break", "break");
80 } 126 }
81 } 127 }
128 if ctx.has_item_list_or_source_file_parent && !ctx.has_trait_parent {
129 add_keyword(ctx, acc, "pub", "pub ")
130 }
131
132 if !ctx.is_trivial_path {
133 return;
134 }
135 let fn_def = match &ctx.function_syntax {
136 Some(it) => it,
137 None => return,
138 };
82 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt)); 139 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
83} 140}
84 141
85fn is_in_loop_body(leaf: &SyntaxToken) -> bool { 142fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
86 // FIXME move this to CompletionContext and make it handle macros 143 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
87 for node in leaf.parent().ancestors() { 144 .kind(CompletionItemKind::Keyword);
88 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { 145
89 break; 146 match ctx.config.snippet_cap {
90 } 147 Some(cap) => res.insert_snippet(cap, snippet),
91 let loop_body = match_ast! { 148 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
92 match node {
93 ast::ForExpr(it) => it.loop_body(),
94 ast::WhileExpr(it) => it.loop_body(),
95 ast::LoopExpr(it) => it.loop_body(),
96 _ => None,
97 }
98 };
99 if let Some(body) = loop_body {
100 if body.syntax().text_range().contains_range(leaf.text_range()) {
101 return true;
102 }
103 }
104 } 149 }
105 false 150 .build()
151}
152
153fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
154 acc.add(keyword(ctx, kw, snippet));
106} 155}
107 156
108fn complete_return( 157fn complete_return(
@@ -121,327 +170,156 @@ fn complete_return(
121 170
122#[cfg(test)] 171#[cfg(test)]
123mod tests { 172mod tests {
124 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 173 use crate::completion::{test_utils::completion_list, CompletionKind};
125 use insta::assert_debug_snapshot; 174 use insta::assert_snapshot;
126 175
127 fn do_keyword_completion(code: &str) -> Vec<CompletionItem> { 176 fn get_keyword_completions(code: &str) -> String {
128 do_completion(code, CompletionKind::Keyword) 177 completion_list(code, CompletionKind::Keyword)
129 } 178 }
130 179
131 #[test] 180 #[test]
132 fn completes_keywords_in_use_stmt() { 181 fn test_keywords_in_use_stmt() {
133 assert_debug_snapshot!( 182 assert_snapshot!(
134 do_keyword_completion( 183 get_keyword_completions(r"use <|>"),
135 r"
136 use <|>
137 ",
138 ),
139 @r###" 184 @r###"
140 [ 185 kw crate
141 CompletionItem { 186 kw self
142 label: "crate", 187 kw super
143 source_range: 21..21,
144 delete: 21..21,
145 insert: "crate::",
146 kind: Keyword,
147 },
148 CompletionItem {
149 label: "self",
150 source_range: 21..21,
151 delete: 21..21,
152 insert: "self",
153 kind: Keyword,
154 },
155 CompletionItem {
156 label: "super",
157 source_range: 21..21,
158 delete: 21..21,
159 insert: "super::",
160 kind: Keyword,
161 },
162 ]
163 "### 188 "###
164 ); 189 );
165 190
166 assert_debug_snapshot!( 191 assert_snapshot!(
167 do_keyword_completion( 192 get_keyword_completions(r"use a::<|>"),
168 r"
169 use a::<|>
170 ",
171 ),
172 @r###" 193 @r###"
173 [ 194 kw self
174 CompletionItem { 195 kw super
175 label: "self",
176 source_range: 24..24,
177 delete: 24..24,
178 insert: "self",
179 kind: Keyword,
180 },
181 CompletionItem {
182 label: "super",
183 source_range: 24..24,
184 delete: 24..24,
185 insert: "super::",
186 kind: Keyword,
187 },
188 ]
189 "### 196 "###
190 ); 197 );
191 198
192 assert_debug_snapshot!( 199 assert_snapshot!(
193 do_keyword_completion( 200 get_keyword_completions(r"use a::{b, <|>}"),
194 r"
195 use a::{b, <|>}
196 ",
197 ),
198 @r###" 201 @r###"
199 [ 202 kw self
200 CompletionItem { 203 kw super
201 label: "self",
202 source_range: 28..28,
203 delete: 28..28,
204 insert: "self",
205 kind: Keyword,
206 },
207 CompletionItem {
208 label: "super",
209 source_range: 28..28,
210 delete: 28..28,
211 insert: "super::",
212 kind: Keyword,
213 },
214 ]
215 "### 204 "###
216 ); 205 );
217 } 206 }
218 207
219 #[test] 208 #[test]
220 fn completes_various_keywords_in_function() { 209 fn test_keywords_at_source_file_level() {
221 assert_debug_snapshot!( 210 assert_snapshot!(
222 do_keyword_completion( 211 get_keyword_completions(r"m<|>"),
223 r"
224 fn quux() {
225 <|>
226 }
227 ",
228 ),
229 @r###" 212 @r###"
230 [ 213 kw const
231 CompletionItem { 214 kw enum
232 label: "if", 215 kw extern
233 source_range: 49..49, 216 kw fn
234 delete: 49..49, 217 kw impl
235 insert: "if $0 {}", 218 kw mod
236 kind: Keyword, 219 kw pub
237 }, 220 kw static
238 CompletionItem { 221 kw struct
239 label: "loop", 222 kw trait
240 source_range: 49..49, 223 kw type
241 delete: 49..49, 224 kw union
242 insert: "loop {$0}", 225 kw unsafe
243 kind: Keyword, 226 kw use
244 },
245 CompletionItem {
246 label: "match",
247 source_range: 49..49,
248 delete: 49..49,
249 insert: "match $0 {}",
250 kind: Keyword,
251 },
252 CompletionItem {
253 label: "return",
254 source_range: 49..49,
255 delete: 49..49,
256 insert: "return;",
257 kind: Keyword,
258 },
259 CompletionItem {
260 label: "while",
261 source_range: 49..49,
262 delete: 49..49,
263 insert: "while $0 {}",
264 kind: Keyword,
265 },
266 ]
267 "### 227 "###
268 ); 228 );
269 } 229 }
270 230
271 #[test] 231 #[test]
272 fn completes_else_after_if() { 232 fn test_keywords_in_function() {
273 assert_debug_snapshot!( 233 assert_snapshot!(
274 do_keyword_completion( 234 get_keyword_completions(r"fn quux() { <|> }"),
275 r"
276 fn quux() {
277 if true {
278 ()
279 } <|>
280 }
281 ",
282 ),
283 @r###" 235 @r###"
284 [ 236 kw const
285 CompletionItem { 237 kw extern
286 label: "else", 238 kw fn
287 source_range: 108..108, 239 kw if
288 delete: 108..108, 240 kw if let
289 insert: "else {$0}", 241 kw impl
290 kind: Keyword, 242 kw let
291 }, 243 kw loop
292 CompletionItem { 244 kw match
293 label: "else if", 245 kw mod
294 source_range: 108..108, 246 kw return
295 delete: 108..108, 247 kw static
296 insert: "else if $0 {}", 248 kw trait
297 kind: Keyword, 249 kw type
298 }, 250 kw unsafe
299 CompletionItem { 251 kw use
300 label: "if", 252 kw while
301 source_range: 108..108,
302 delete: 108..108,
303 insert: "if $0 {}",
304 kind: Keyword,
305 },
306 CompletionItem {
307 label: "loop",
308 source_range: 108..108,
309 delete: 108..108,
310 insert: "loop {$0}",
311 kind: Keyword,
312 },
313 CompletionItem {
314 label: "match",
315 source_range: 108..108,
316 delete: 108..108,
317 insert: "match $0 {}",
318 kind: Keyword,
319 },
320 CompletionItem {
321 label: "return",
322 source_range: 108..108,
323 delete: 108..108,
324 insert: "return;",
325 kind: Keyword,
326 },
327 CompletionItem {
328 label: "while",
329 source_range: 108..108,
330 delete: 108..108,
331 insert: "while $0 {}",
332 kind: Keyword,
333 },
334 ]
335 "### 253 "###
336 ); 254 );
337 } 255 }
338 256
339 #[test] 257 #[test]
340 fn test_completion_return_value() { 258 fn test_keywords_inside_block() {
341 assert_debug_snapshot!( 259 assert_snapshot!(
342 do_keyword_completion( 260 get_keyword_completions(r"fn quux() { if true { <|> } }"),
343 r"
344 fn quux() -> i32 {
345 <|>
346 92
347 }
348 ",
349 ),
350 @r###" 261 @r###"
351 [ 262 kw const
352 CompletionItem { 263 kw extern
353 label: "if", 264 kw fn
354 source_range: 56..56, 265 kw if
355 delete: 56..56, 266 kw if let
356 insert: "if $0 {}", 267 kw impl
357 kind: Keyword, 268 kw let
358 }, 269 kw loop
359 CompletionItem { 270 kw match
360 label: "loop", 271 kw mod
361 source_range: 56..56, 272 kw return
362 delete: 56..56, 273 kw static
363 insert: "loop {$0}", 274 kw trait
364 kind: Keyword, 275 kw type
365 }, 276 kw unsafe
366 CompletionItem { 277 kw use
367 label: "match", 278 kw while
368 source_range: 56..56,
369 delete: 56..56,
370 insert: "match $0 {}",
371 kind: Keyword,
372 },
373 CompletionItem {
374 label: "return",
375 source_range: 56..56,
376 delete: 56..56,
377 insert: "return $0;",
378 kind: Keyword,
379 },
380 CompletionItem {
381 label: "while",
382 source_range: 56..56,
383 delete: 56..56,
384 insert: "while $0 {}",
385 kind: Keyword,
386 },
387 ]
388 "### 279 "###
389 ); 280 );
390 assert_debug_snapshot!( 281 }
391 do_keyword_completion( 282
283 #[test]
284 fn test_keywords_after_if() {
285 assert_snapshot!(
286 get_keyword_completions(
392 r" 287 r"
393 fn quux() { 288 fn quux() {
394 <|> 289 if true {
395 92 290 ()
291 } <|>
396 } 292 }
397 ", 293 ",
398 ), 294 ),
399 @r###" 295 @r###"
400 [ 296 kw const
401 CompletionItem { 297 kw else
402 label: "if", 298 kw else if
403 source_range: 49..49, 299 kw extern
404 delete: 49..49, 300 kw fn
405 insert: "if $0 {}", 301 kw if
406 kind: Keyword, 302 kw if let
407 }, 303 kw impl
408 CompletionItem { 304 kw let
409 label: "loop", 305 kw loop
410 source_range: 49..49, 306 kw match
411 delete: 49..49, 307 kw mod
412 insert: "loop {$0}", 308 kw return
413 kind: Keyword, 309 kw static
414 }, 310 kw trait
415 CompletionItem { 311 kw type
416 label: "match", 312 kw unsafe
417 source_range: 49..49, 313 kw use
418 delete: 49..49, 314 kw while
419 insert: "match $0 {}",
420 kind: Keyword,
421 },
422 CompletionItem {
423 label: "return",
424 source_range: 49..49,
425 delete: 49..49,
426 insert: "return;",
427 kind: Keyword,
428 },
429 CompletionItem {
430 label: "while",
431 source_range: 49..49,
432 delete: 49..49,
433 insert: "while $0 {}",
434 kind: Keyword,
435 },
436 ]
437 "### 315 "###
438 ); 316 );
439 } 317 }
440 318
441 #[test] 319 #[test]
442 fn dont_add_semi_after_return_if_not_a_statement() { 320 fn test_keywords_in_match_arm() {
443 assert_debug_snapshot!( 321 assert_snapshot!(
444 do_keyword_completion( 322 get_keyword_completions(
445 r" 323 r"
446 fn quux() -> i32 { 324 fn quux() -> i32 {
447 match () { 325 match () {
@@ -451,336 +329,130 @@ mod tests {
451 ", 329 ",
452 ), 330 ),
453 @r###" 331 @r###"
454 [ 332 kw if
455 CompletionItem { 333 kw if let
456 label: "if", 334 kw loop
457 source_range: 97..97, 335 kw match
458 delete: 97..97, 336 kw return
459 insert: "if $0 {}", 337 kw unsafe
460 kind: Keyword,
461 },
462 CompletionItem {
463 label: "loop",
464 source_range: 97..97,
465 delete: 97..97,
466 insert: "loop {$0}",
467 kind: Keyword,
468 },
469 CompletionItem {
470 label: "match",
471 source_range: 97..97,
472 delete: 97..97,
473 insert: "match $0 {}",
474 kind: Keyword,
475 },
476 CompletionItem {
477 label: "return",
478 source_range: 97..97,
479 delete: 97..97,
480 insert: "return $0",
481 kind: Keyword,
482 },
483 CompletionItem {
484 label: "while",
485 source_range: 97..97,
486 delete: 97..97,
487 insert: "while $0 {}",
488 kind: Keyword,
489 },
490 ]
491 "### 338 "###
492 ); 339 );
493 } 340 }
494 341
495 #[test] 342 #[test]
496 fn last_return_in_block_has_semi() { 343 fn test_keywords_in_trait_def() {
497 assert_debug_snapshot!( 344 assert_snapshot!(
498 do_keyword_completion( 345 get_keyword_completions(r"trait My { <|> }"),
499 r"
500 fn quux() -> i32 {
501 if condition {
502 <|>
503 }
504 }
505 ",
506 ),
507 @r###" 346 @r###"
508 [ 347 kw const
509 CompletionItem { 348 kw fn
510 label: "if", 349 kw type
511 source_range: 95..95, 350 kw unsafe
512 delete: 95..95,
513 insert: "if $0 {}",
514 kind: Keyword,
515 },
516 CompletionItem {
517 label: "loop",
518 source_range: 95..95,
519 delete: 95..95,
520 insert: "loop {$0}",
521 kind: Keyword,
522 },
523 CompletionItem {
524 label: "match",
525 source_range: 95..95,
526 delete: 95..95,
527 insert: "match $0 {}",
528 kind: Keyword,
529 },
530 CompletionItem {
531 label: "return",
532 source_range: 95..95,
533 delete: 95..95,
534 insert: "return $0;",
535 kind: Keyword,
536 },
537 CompletionItem {
538 label: "while",
539 source_range: 95..95,
540 delete: 95..95,
541 insert: "while $0 {}",
542 kind: Keyword,
543 },
544 ]
545 "### 351 "###
546 ); 352 );
547 assert_debug_snapshot!( 353 }
548 do_keyword_completion( 354
549 r" 355 #[test]
550 fn quux() -> i32 { 356 fn test_keywords_in_impl_def() {
551 if condition { 357 assert_snapshot!(
552 <|> 358 get_keyword_completions(r"impl My { <|> }"),
553 }
554 let x = 92;
555 x
556 }
557 ",
558 ),
559 @r###" 359 @r###"
560 [ 360 kw const
561 CompletionItem { 361 kw fn
562 label: "if", 362 kw pub
563 source_range: 95..95, 363 kw type
564 delete: 95..95, 364 kw unsafe
565 insert: "if $0 {}",
566 kind: Keyword,
567 },
568 CompletionItem {
569 label: "loop",
570 source_range: 95..95,
571 delete: 95..95,
572 insert: "loop {$0}",
573 kind: Keyword,
574 },
575 CompletionItem {
576 label: "match",
577 source_range: 95..95,
578 delete: 95..95,
579 insert: "match $0 {}",
580 kind: Keyword,
581 },
582 CompletionItem {
583 label: "return",
584 source_range: 95..95,
585 delete: 95..95,
586 insert: "return $0;",
587 kind: Keyword,
588 },
589 CompletionItem {
590 label: "while",
591 source_range: 95..95,
592 delete: 95..95,
593 insert: "while $0 {}",
594 kind: Keyword,
595 },
596 ]
597 "### 365 "###
598 ); 366 );
599 } 367 }
600 368
601 #[test] 369 #[test]
602 fn completes_break_and_continue_in_loops() { 370 fn test_keywords_in_loop() {
603 assert_debug_snapshot!( 371 assert_snapshot!(
604 do_keyword_completion( 372 get_keyword_completions(r"fn my() { loop { <|> } }"),
605 r"
606 fn quux() -> i32 {
607 loop { <|> }
608 }
609 ",
610 ),
611 @r###" 373 @r###"
612 [ 374 kw break
613 CompletionItem { 375 kw const
614 label: "break", 376 kw continue
615 source_range: 63..63, 377 kw extern
616 delete: 63..63, 378 kw fn
617 insert: "break;", 379 kw if
618 kind: Keyword, 380 kw if let
619 }, 381 kw impl
620 CompletionItem { 382 kw let
621 label: "continue", 383 kw loop
622 source_range: 63..63, 384 kw match
623 delete: 63..63, 385 kw mod
624 insert: "continue;", 386 kw return
625 kind: Keyword, 387 kw static
626 }, 388 kw trait
627 CompletionItem { 389 kw type
628 label: "if", 390 kw unsafe
629 source_range: 63..63, 391 kw use
630 delete: 63..63, 392 kw while
631 insert: "if $0 {}",
632 kind: Keyword,
633 },
634 CompletionItem {
635 label: "loop",
636 source_range: 63..63,
637 delete: 63..63,
638 insert: "loop {$0}",
639 kind: Keyword,
640 },
641 CompletionItem {
642 label: "match",
643 source_range: 63..63,
644 delete: 63..63,
645 insert: "match $0 {}",
646 kind: Keyword,
647 },
648 CompletionItem {
649 label: "return",
650 source_range: 63..63,
651 delete: 63..63,
652 insert: "return $0;",
653 kind: Keyword,
654 },
655 CompletionItem {
656 label: "while",
657 source_range: 63..63,
658 delete: 63..63,
659 insert: "while $0 {}",
660 kind: Keyword,
661 },
662 ]
663 "### 393 "###
664 ); 394 );
395 }
665 396
666 // No completion: lambda isolates control flow 397 #[test]
667 assert_debug_snapshot!( 398 fn test_keywords_after_unsafe_in_item_list() {
668 do_keyword_completion( 399 assert_snapshot!(
669 r" 400 get_keyword_completions(r"unsafe <|>"),
670 fn quux() -> i32 {
671 loop { || { <|> } }
672 }
673 ",
674 ),
675 @r###" 401 @r###"
676 [ 402 kw fn
677 CompletionItem { 403 kw impl
678 label: "if", 404 kw trait
679 source_range: 68..68,
680 delete: 68..68,
681 insert: "if $0 {}",
682 kind: Keyword,
683 },
684 CompletionItem {
685 label: "loop",
686 source_range: 68..68,
687 delete: 68..68,
688 insert: "loop {$0}",
689 kind: Keyword,
690 },
691 CompletionItem {
692 label: "match",
693 source_range: 68..68,
694 delete: 68..68,
695 insert: "match $0 {}",
696 kind: Keyword,
697 },
698 CompletionItem {
699 label: "return",
700 source_range: 68..68,
701 delete: 68..68,
702 insert: "return $0;",
703 kind: Keyword,
704 },
705 CompletionItem {
706 label: "while",
707 source_range: 68..68,
708 delete: 68..68,
709 insert: "while $0 {}",
710 kind: Keyword,
711 },
712 ]
713 "### 405 "###
714 ); 406 );
715 } 407 }
716 408
717 #[test] 409 #[test]
718 fn no_semi_after_break_continue_in_expr() { 410 fn test_keywords_after_unsafe_in_block_expr() {
719 assert_debug_snapshot!( 411 assert_snapshot!(
720 do_keyword_completion( 412 get_keyword_completions(r"fn my_fn() { unsafe <|> }"),
721 r"
722 fn f() {
723 loop {
724 match () {
725 () => br<|>
726 }
727 }
728 }
729 ",
730 ),
731 @r###" 413 @r###"
732 [ 414 kw fn
733 CompletionItem { 415 kw impl
734 label: "break", 416 kw trait
735 source_range: 122..124,
736 delete: 122..124,
737 insert: "break",
738 kind: Keyword,
739 },
740 CompletionItem {
741 label: "continue",
742 source_range: 122..124,
743 delete: 122..124,
744 insert: "continue",
745 kind: Keyword,
746 },
747 CompletionItem {
748 label: "if",
749 source_range: 122..124,
750 delete: 122..124,
751 insert: "if $0 {}",
752 kind: Keyword,
753 },
754 CompletionItem {
755 label: "loop",
756 source_range: 122..124,
757 delete: 122..124,
758 insert: "loop {$0}",
759 kind: Keyword,
760 },
761 CompletionItem {
762 label: "match",
763 source_range: 122..124,
764 delete: 122..124,
765 insert: "match $0 {}",
766 kind: Keyword,
767 },
768 CompletionItem {
769 label: "return",
770 source_range: 122..124,
771 delete: 122..124,
772 insert: "return",
773 kind: Keyword,
774 },
775 CompletionItem {
776 label: "while",
777 source_range: 122..124,
778 delete: 122..124,
779 insert: "while $0 {}",
780 kind: Keyword,
781 },
782 ]
783 "### 417 "###
784 ) 418 );
419 }
420
421 #[test]
422 fn test_mut_in_ref_and_in_fn_parameters_list() {
423 assert_snapshot!(
424 get_keyword_completions(r"fn my_fn(&<|>) {}"),
425 @r###"
426 kw mut
427 "###
428 );
429 assert_snapshot!(
430 get_keyword_completions(r"fn my_fn(<|>) {}"),
431 @r###"
432 kw mut
433 "###
434 );
435 assert_snapshot!(
436 get_keyword_completions(r"fn my_fn() { let &<|> }"),
437 @r###"
438 kw mut
439 "###
440 );
441 }
442
443 #[test]
444 fn test_where_keyword() {
445 assert_snapshot!(
446 get_keyword_completions(r"trait A <|>"),
447 @r###"
448 kw where
449 "###
450 );
451 assert_snapshot!(
452 get_keyword_completions(r"impl A <|>"),
453 @r###"
454 kw where
455 "###
456 );
785 } 457 }
786} 458}
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 59b58bf98..b878aeb0a 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -91,7 +91,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
91 &dot_receiver, 91 &dot_receiver,
92 "if", 92 "if",
93 "if expr {}", 93 "if expr {}",
94 &format!("if {} {{$0}}", receiver_text), 94 &format!("if {} {{\n $0\n}}", receiver_text),
95 ) 95 )
96 .add_to(acc); 96 .add_to(acc);
97 postfix_snippet( 97 postfix_snippet(
@@ -100,7 +100,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
100 &dot_receiver, 100 &dot_receiver,
101 "while", 101 "while",
102 "while expr {}", 102 "while expr {}",
103 &format!("while {} {{\n$0\n}}", receiver_text), 103 &format!("while {} {{\n $0\n}}", receiver_text),
104 ) 104 )
105 .add_to(acc); 105 .add_to(acc);
106 } 106 }
@@ -283,7 +283,7 @@ mod tests {
283 label: "if", 283 label: "if",
284 source_range: 89..89, 284 source_range: 89..89,
285 delete: 85..89, 285 delete: 85..89,
286 insert: "if bar {$0}", 286 insert: "if bar {\n $0\n}",
287 detail: "if expr {}", 287 detail: "if expr {}",
288 }, 288 },
289 CompletionItem { 289 CompletionItem {
@@ -318,7 +318,7 @@ mod tests {
318 label: "while", 318 label: "while",
319 source_range: 89..89, 319 source_range: 89..89,
320 delete: 85..89, 320 delete: 85..89,
321 insert: "while bar {\n$0\n}", 321 insert: "while bar {\n $0\n}",
322 detail: "while expr {}", 322 detail: "while expr {}",
323 }, 323 },
324 ] 324 ]
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index c4646b727..560fb19e6 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -5,12 +5,17 @@ use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::{find_covering_element, find_node_at_offset},
8 ast, match_ast, AstNode, 8 ast, match_ast, AstNode, NodeOrToken,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize, 10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11}; 11};
12use ra_text_edit::Indel; 12use ra_text_edit::Indel;
13 13
14use super::patterns::{
15 has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_impl_parent,
16 has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling,
17 has_trait_parent, if_is_prev, is_in_loop_body, is_match_arm, unsafe_is_prev,
18};
14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; 19use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition};
15use test_utils::mark; 20use test_utils::mark;
16 21
@@ -60,6 +65,18 @@ pub(crate) struct CompletionContext<'a> {
60 pub(super) is_path_type: bool, 65 pub(super) is_path_type: bool,
61 pub(super) has_type_args: bool, 66 pub(super) has_type_args: bool,
62 pub(super) attribute_under_caret: Option<ast::Attr>, 67 pub(super) attribute_under_caret: Option<ast::Attr>,
68 pub(super) unsafe_is_prev: bool,
69 pub(super) if_is_prev: bool,
70 pub(super) block_expr_parent: bool,
71 pub(super) bind_pat_parent: bool,
72 pub(super) ref_pat_parent: bool,
73 pub(super) in_loop_body: bool,
74 pub(super) has_trait_parent: bool,
75 pub(super) has_impl_parent: bool,
76 pub(super) trait_as_prev_sibling: bool,
77 pub(super) impl_as_prev_sibling: bool,
78 pub(super) is_match_arm: bool,
79 pub(super) has_item_list_or_source_file_parent: bool,
63} 80}
64 81
65impl<'a> CompletionContext<'a> { 82impl<'a> CompletionContext<'a> {
@@ -118,6 +135,18 @@ impl<'a> CompletionContext<'a> {
118 has_type_args: false, 135 has_type_args: false,
119 dot_receiver_is_ambiguous_float_literal: false, 136 dot_receiver_is_ambiguous_float_literal: false,
120 attribute_under_caret: None, 137 attribute_under_caret: None,
138 unsafe_is_prev: false,
139 in_loop_body: false,
140 ref_pat_parent: false,
141 bind_pat_parent: false,
142 block_expr_parent: false,
143 has_trait_parent: false,
144 has_impl_parent: false,
145 trait_as_prev_sibling: false,
146 impl_as_prev_sibling: false,
147 if_is_prev: false,
148 is_match_arm: false,
149 has_item_list_or_source_file_parent: false,
121 }; 150 };
122 151
123 let mut original_file = original_file.syntax().clone(); 152 let mut original_file = original_file.syntax().clone();
@@ -159,7 +188,7 @@ impl<'a> CompletionContext<'a> {
159 break; 188 break;
160 } 189 }
161 } 190 }
162 191 ctx.fill_keyword_patterns(&hypothetical_file, offset);
163 ctx.fill(&original_file, hypothetical_file, offset); 192 ctx.fill(&original_file, hypothetical_file, offset);
164 Some(ctx) 193 Some(ctx)
165 } 194 }
@@ -188,6 +217,24 @@ impl<'a> CompletionContext<'a> {
188 self.sema.scope_at_offset(&self.token.parent(), self.offset) 217 self.sema.scope_at_offset(&self.token.parent(), self.offset)
189 } 218 }
190 219
220 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
221 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
222 let syntax_element = NodeOrToken::Token(fake_ident_token.clone());
223 self.block_expr_parent = has_block_expr_parent(syntax_element.clone());
224 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone());
225 self.if_is_prev = if_is_prev(syntax_element.clone());
226 self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone());
227 self.ref_pat_parent = has_ref_parent(syntax_element.clone());
228 self.in_loop_body = is_in_loop_body(syntax_element.clone());
229 self.has_trait_parent = has_trait_parent(syntax_element.clone());
230 self.has_impl_parent = has_impl_parent(syntax_element.clone());
231 self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone());
232 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone());
233 self.is_match_arm = is_match_arm(syntax_element.clone());
234 self.has_item_list_or_source_file_parent =
235 has_item_list_or_source_file_parent(syntax_element.clone());
236 }
237
191 fn fill( 238 fn fill(
192 &mut self, 239 &mut self,
193 original_file: &SyntaxNode, 240 original_file: &SyntaxNode,
@@ -334,6 +381,7 @@ impl<'a> CompletionContext<'a> {
334 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 381 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
335 self.has_type_args = segment.type_arg_list().is_some(); 382 self.has_type_args = segment.type_arg_list().is_some();
336 383
384 #[allow(deprecated)]
337 if let Some(path) = hir::Path::from_ast(path.clone()) { 385 if let Some(path) = hir::Path::from_ast(path.clone()) {
338 if let Some(path_prefix) = path.qualifier() { 386 if let Some(path_prefix) = path.qualifier() {
339 self.path_prefix = Some(path_prefix); 387 self.path_prefix = Some(path_prefix);
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index cfb7c1e38..98348b349 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -125,6 +125,32 @@ pub enum CompletionItemKind {
125 Attribute, 125 Attribute,
126} 126}
127 127
128impl CompletionItemKind {
129 #[cfg(test)]
130 pub(crate) fn tag(&self) -> &'static str {
131 match self {
132 CompletionItemKind::Snippet => "sn",
133 CompletionItemKind::Keyword => "kw",
134 CompletionItemKind::Module => "md",
135 CompletionItemKind::Function => "fn",
136 CompletionItemKind::BuiltinType => "bt",
137 CompletionItemKind::Struct => "st",
138 CompletionItemKind::Enum => "en",
139 CompletionItemKind::EnumVariant => "ev",
140 CompletionItemKind::Binding => "bn",
141 CompletionItemKind::Field => "fd",
142 CompletionItemKind::Static => "sc",
143 CompletionItemKind::Const => "ct",
144 CompletionItemKind::Trait => "tt",
145 CompletionItemKind::TypeAlias => "ta",
146 CompletionItemKind::Method => "me",
147 CompletionItemKind::TypeParam => "tp",
148 CompletionItemKind::Macro => "ma",
149 CompletionItemKind::Attribute => "at",
150 }
151 }
152}
153
128#[derive(Debug, PartialEq, Eq, Copy, Clone)] 154#[derive(Debug, PartialEq, Eq, Copy, Clone)]
129pub(crate) enum CompletionKind { 155pub(crate) enum CompletionKind {
130 /// Parser-based keyword completion. 156 /// Parser-based keyword completion.
diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs
new file mode 100644
index 000000000..b2fe13280
--- /dev/null
+++ b/crates/ra_ide/src/completion/patterns.rs
@@ -0,0 +1,194 @@
1//! Patterns telling us certain facts about current syntax element, they are used in completion context
2
3use ra_syntax::{
4 algo::non_trivia_sibling,
5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken,
9};
10
11#[cfg(test)]
12use crate::completion::test_utils::check_pattern_is_applicable;
13
14pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool {
15 not_same_range_ancestor(element)
16 .filter(|it| it.kind() == ITEM_LIST)
17 .and_then(|it| it.parent())
18 .filter(|it| it.kind() == TRAIT_DEF)
19 .is_some()
20}
21#[test]
22fn test_has_trait_parent() {
23 check_pattern_is_applicable(r"trait A { f<|> }", has_trait_parent);
24}
25
26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool {
27 not_same_range_ancestor(element)
28 .filter(|it| it.kind() == ITEM_LIST)
29 .and_then(|it| it.parent())
30 .filter(|it| it.kind() == IMPL_DEF)
31 .is_some()
32}
33#[test]
34fn test_has_impl_parent() {
35 check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent);
36}
37
38pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
39 not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some()
40}
41#[test]
42fn test_has_block_expr_parent() {
43 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f<|> }", has_block_expr_parent);
44}
45
46pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
47 element.ancestors().find(|it| it.kind() == BIND_PAT).is_some()
48}
49#[test]
50fn test_has_bind_pat_parent() {
51 check_pattern_is_applicable(r"fn my_fn(m<|>) {}", has_bind_pat_parent);
52 check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent);
53}
54
55pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
56 not_same_range_ancestor(element)
57 .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR)
58 .is_some()
59}
60#[test]
61fn test_has_ref_parent() {
62 check_pattern_is_applicable(r"fn my_fn(&m<|>) {}", has_ref_parent);
63 check_pattern_is_applicable(r"fn my() { let &m<|> }", has_ref_parent);
64}
65
66pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool {
67 let ancestor = not_same_range_ancestor(element);
68 if !ancestor.is_some() {
69 return true;
70 }
71 ancestor.filter(|it| it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST).is_some()
72}
73#[test]
74fn test_has_item_list_or_source_file_parent() {
75 check_pattern_is_applicable(r"i<|>", has_item_list_or_source_file_parent);
76 check_pattern_is_applicable(r"impl { f<|> }", has_item_list_or_source_file_parent);
77}
78
79pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
80 not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some()
81 && previous_sibling_or_ancestor_sibling(element)
82 .and_then(|it| it.into_token())
83 .filter(|it| it.kind() == FAT_ARROW)
84 .is_some()
85}
86#[test]
87fn test_is_match_arm() {
88 check_pattern_is_applicable(r"fn my_fn() { match () { () => m<|> } }", is_match_arm);
89}
90
91pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool {
92 element
93 .into_token()
94 .and_then(|it| previous_non_trivia_token(it))
95 .filter(|it| it.kind() == UNSAFE_KW)
96 .is_some()
97}
98#[test]
99fn test_unsafe_is_prev() {
100 check_pattern_is_applicable(r"unsafe i<|>", unsafe_is_prev);
101}
102
103pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
104 element
105 .into_token()
106 .and_then(|it| previous_non_trivia_token(it))
107 .filter(|it| it.kind() == IF_KW)
108 .is_some()
109}
110#[test]
111fn test_if_is_prev() {
112 check_pattern_is_applicable(r"if l<|>", if_is_prev);
113}
114
115pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool {
116 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT_DEF).is_some()
117}
118#[test]
119fn test_has_trait_as_prev_sibling() {
120 check_pattern_is_applicable(r"trait A w<|> {}", has_trait_as_prev_sibling);
121}
122
123pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool {
124 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL_DEF).is_some()
125}
126#[test]
127fn test_has_impl_as_prev_sibling() {
128 check_pattern_is_applicable(r"impl A w<|> {}", has_impl_as_prev_sibling);
129}
130
131pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
132 let leaf = match element {
133 NodeOrToken::Node(node) => node,
134 NodeOrToken::Token(token) => token.parent(),
135 };
136 for node in leaf.ancestors() {
137 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
138 break;
139 }
140 let loop_body = match_ast! {
141 match node {
142 ast::ForExpr(it) => it.loop_body(),
143 ast::WhileExpr(it) => it.loop_body(),
144 ast::LoopExpr(it) => it.loop_body(),
145 _ => None,
146 }
147 };
148 if let Some(body) = loop_body {
149 if body.syntax().text_range().contains_range(leaf.text_range()) {
150 return true;
151 }
152 }
153 }
154 false
155}
156
157fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
158 element
159 .ancestors()
160 .take_while(|it| it.text_range() == element.text_range())
161 .last()
162 .and_then(|it| it.parent())
163}
164
165fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
166 let mut token = token.prev_token();
167 while let Some(inner) = token.clone() {
168 if !inner.kind().is_trivia() {
169 return Some(inner);
170 } else {
171 token = inner.prev_token();
172 }
173 }
174 None
175}
176
177fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<SyntaxElement> {
178 let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev);
179 if let Some(sibling) = token_sibling {
180 Some(sibling)
181 } else {
182 // if not trying to find first ancestor which has such a sibling
183 let node = match element {
184 NodeOrToken::Node(node) => node,
185 NodeOrToken::Token(token) => token.parent(),
186 };
187 let range = node.text_range();
188 let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?;
189 let prev_sibling_node = top_node.ancestors().find(|it| {
190 non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some()
191 })?;
192 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)
193 }
194}
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index bf22452a2..1e16a43ca 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -5,25 +5,63 @@ use crate::{
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};
8use hir::Semantics;
9use ra_syntax::{AstNode, NodeOrToken, SyntaxElement};
8 10
9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 11pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
10 do_completion_with_options(code, kind, &CompletionConfig::default()) 12 do_completion_with_options(code, kind, &CompletionConfig::default())
11} 13}
12 14
15pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
16 completion_list_with_options(code, kind, &CompletionConfig::default())
17}
18
13pub(crate) fn do_completion_with_options( 19pub(crate) fn do_completion_with_options(
14 code: &str, 20 code: &str,
15 kind: CompletionKind, 21 kind: CompletionKind,
16 options: &CompletionConfig, 22 options: &CompletionConfig,
17) -> Vec<CompletionItem> { 23) -> Vec<CompletionItem> {
24 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(code, options)
25 .into_iter()
26 .filter(|c| c.completion_kind == kind)
27 .collect();
28 kind_completions.sort_by(|l, r| l.label().cmp(r.label()));
29 kind_completions
30}
31
32fn get_all_completion_items(code: &str, options: &CompletionConfig) -> Vec<CompletionItem> {
18 let (analysis, position) = if code.contains("//-") { 33 let (analysis, position) = if code.contains("//-") {
19 analysis_and_position(code) 34 analysis_and_position(code)
20 } else { 35 } else {
21 single_file_with_position(code) 36 single_file_with_position(code)
22 }; 37 };
23 let completions = analysis.completions(options, position).unwrap().unwrap(); 38 analysis.completions(options, position).unwrap().unwrap().into()
24 let completion_items: Vec<CompletionItem> = completions.into(); 39}
25 let mut kind_completions: Vec<CompletionItem> = 40
26 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); 41pub(crate) fn completion_list_with_options(
42 code: &str,
43 kind: CompletionKind,
44 options: &CompletionConfig,
45) -> String {
46 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(code, options)
47 .into_iter()
48 .filter(|c| c.completion_kind == kind)
49 .collect();
27 kind_completions.sort_by_key(|c| c.label().to_owned()); 50 kind_completions.sort_by_key(|c| c.label().to_owned());
28 kind_completions 51 kind_completions
52 .into_iter()
53 .map(|it| format!("{} {}\n", it.kind().unwrap().tag(), it.label()))
54 .collect()
55}
56
57pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) {
58 let (analysis, pos) = single_file_with_position(code);
59 analysis
60 .with_db(|db| {
61 let sema = Semantics::new(db);
62 let original_file = sema.parse(pos.file_id);
63 let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap();
64 assert!(check(NodeOrToken::Token(token)));
65 })
66 .unwrap();
29} 67}
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index f44feaf69..fd9abb55b 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -11,7 +11,7 @@ use hir::{
11 Semantics, 11 Semantics,
12}; 12};
13use itertools::Itertools; 13use itertools::Itertools;
14use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; 14use ra_db::SourceDatabase;
15use ra_ide_db::R