aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock34
-rw-r--r--crates/assists/src/handlers/ignore_test.rs83
-rw-r--r--crates/completion/src/completions/unqualified_path.rs4
-rw-r--r--crates/completion/src/config.rs2
-rw-r--r--crates/completion/src/lib.rs7
-rw-r--r--crates/flycheck/Cargo.toml2
-rw-r--r--crates/hir/src/db.rs2
-rw-r--r--crates/hir/src/lib.rs4
-rw-r--r--crates/hir_expand/src/db.rs141
-rw-r--r--crates/hir_expand/src/lib.rs2
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs248
-rw-r--r--crates/hir_ty/src/infer/pat.rs46
-rw-r--r--crates/hir_ty/src/tests/patterns.rs95
-rw-r--r--crates/ide/src/status.rs10
-rw-r--r--crates/ide_db/src/apply_change.rs4
-rw-r--r--crates/ide_db/src/lib.rs2
-rw-r--r--crates/proc_macro_srv/Cargo.toml2
-rw-r--r--crates/project_model/Cargo.toml2
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/config.rs3
-rw-r--r--crates/rust-analyzer/src/main_loop.rs8
-rw-r--r--crates/rust-analyzer/src/to_proto.rs21
-rw-r--r--editors/code/package.json8
23 files changed, 544 insertions, 188 deletions
diff --git a/Cargo.lock b/Cargo.lock
index edadd1057..051d9e734 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -81,9 +81,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
81 81
82[[package]] 82[[package]]
83name = "backtrace" 83name = "backtrace"
84version = "0.3.54" 84version = "0.3.55"
85source = "registry+https://github.com/rust-lang/crates.io-index" 85source = "registry+https://github.com/rust-lang/crates.io-index"
86checksum = "2baad346b2d4e94a24347adeee9c7a93f412ee94b9cc26e5b59dea23848e9f28" 86checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598"
87dependencies = [ 87dependencies = [
88 "addr2line", 88 "addr2line",
89 "cfg-if 1.0.0", 89 "cfg-if 1.0.0",
@@ -139,9 +139,9 @@ dependencies = [
139 139
140[[package]] 140[[package]]
141name = "cc" 141name = "cc"
142version = "1.0.63" 142version = "1.0.65"
143source = "registry+https://github.com/rust-lang/crates.io-index" 143source = "registry+https://github.com/rust-lang/crates.io-index"
144checksum = "ad9c6140b5a2c7db40ea56eb1821245e5362b44385c05b76288b1a599934ac87" 144checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15"
145 145
146[[package]] 146[[package]]
147name = "cfg" 147name = "cfg"
@@ -865,9 +865,9 @@ dependencies = [
865 865
866[[package]] 866[[package]]
867name = "lsp-types" 867name = "lsp-types"
868version = "0.83.1" 868version = "0.84.0"
869source = "registry+https://github.com/rust-lang/crates.io-index" 869source = "registry+https://github.com/rust-lang/crates.io-index"
870checksum = "c4e79f39834b97271f9f5ecec573e42c7d9c5bdbd2620b30a851054ece6aab6d" 870checksum = "3b95be71fe205e44de754185bcf86447b65813ce1ceb298f8d3793ade5fff08d"
871dependencies = [ 871dependencies = [
872 "base64", 872 "base64",
873 "bitflags", 873 "bitflags",
@@ -1072,9 +1072,9 @@ checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
1072 1072
1073[[package]] 1073[[package]]
1074name = "oorandom" 1074name = "oorandom"
1075version = "11.1.2" 1075version = "11.1.3"
1076source = "registry+https://github.com/rust-lang/crates.io-index" 1076source = "registry+https://github.com/rust-lang/crates.io-index"
1077checksum = "a170cebd8021a008ea92e4db85a72f80b35df514ec664b296fdcbb654eac0b2c" 1077checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
1078 1078
1079[[package]] 1079[[package]]
1080name = "parking_lot" 1080name = "parking_lot"
@@ -1165,9 +1165,9 @@ checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1"
1165 1165
1166[[package]] 1166[[package]]
1167name = "pin-project-lite" 1167name = "pin-project-lite"
1168version = "0.1.11" 1168version = "0.2.0"
1169source = "registry+https://github.com/rust-lang/crates.io-index" 1169source = "registry+https://github.com/rust-lang/crates.io-index"
1170checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" 1170checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c"
1171 1171
1172[[package]] 1172[[package]]
1173name = "plain" 1173name = "plain"
@@ -1627,9 +1627,9 @@ version = "0.0.0"
1627 1627
1628[[package]] 1628[[package]]
1629name = "syn" 1629name = "syn"
1630version = "1.0.48" 1630version = "1.0.51"
1631source = "registry+https://github.com/rust-lang/crates.io-index" 1631source = "registry+https://github.com/rust-lang/crates.io-index"
1632checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" 1632checksum = "3b4f34193997d92804d359ed09953e25d5138df6bcc055a71bf68ee89fdf9223"
1633dependencies = [ 1633dependencies = [
1634 "proc-macro2", 1634 "proc-macro2",
1635 "quote", 1635 "quote",
@@ -1740,9 +1740,9 @@ dependencies = [
1740 1740
1741[[package]] 1741[[package]]
1742name = "tinyvec" 1742name = "tinyvec"
1743version = "1.0.1" 1743version = "1.1.0"
1744source = "registry+https://github.com/rust-lang/crates.io-index" 1744source = "registry+https://github.com/rust-lang/crates.io-index"
1745checksum = "b78a366903f506d2ad52ca8dc552102ffdd3e937ba8a227f024dc1d1eae28575" 1745checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f"
1746dependencies = [ 1746dependencies = [
1747 "tinyvec_macros", 1747 "tinyvec_macros",
1748] 1748]
@@ -1762,11 +1762,11 @@ dependencies = [
1762 1762
1763[[package]] 1763[[package]]
1764name = "tracing" 1764name = "tracing"
1765version = "0.1.21" 1765version = "0.1.22"
1766source = "registry+https://github.com/rust-lang/crates.io-index" 1766source = "registry+https://github.com/rust-lang/crates.io-index"
1767checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27" 1767checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3"
1768dependencies = [ 1768dependencies = [
1769 "cfg-if 0.1.10", 1769 "cfg-if 1.0.0",
1770 "pin-project-lite", 1770 "pin-project-lite",
1771 "tracing-attributes", 1771 "tracing-attributes",
1772 "tracing-core", 1772 "tracing-core",
diff --git a/crates/assists/src/handlers/ignore_test.rs b/crates/assists/src/handlers/ignore_test.rs
index d2339184f..5096a0005 100644
--- a/crates/assists/src/handlers/ignore_test.rs
+++ b/crates/assists/src/handlers/ignore_test.rs
@@ -1,4 +1,7 @@
1use syntax::{ast, AstNode}; 1use syntax::{
2 ast::{self, AttrsOwner},
3 AstNode, AstToken,
4};
2 5
3use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists}; 6use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists};
4 7
@@ -25,10 +28,76 @@ pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
25 let func = attr.syntax().parent().and_then(ast::Fn::cast)?; 28 let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
26 let attr = test_related_attribute(&func)?; 29 let attr = test_related_attribute(&func)?;
27 30
28 acc.add( 31 match has_ignore_attribute(&func) {
29 AssistId("ignore_test", AssistKind::None), 32 None => acc.add(
30 "Ignore this test", 33 AssistId("ignore_test", AssistKind::None),
31 attr.syntax().text_range(), 34 "Ignore this test",
32 |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")), 35 attr.syntax().text_range(),
33 ) 36 |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")),
37 ),
38 Some(ignore_attr) => acc.add(
39 AssistId("unignore_test", AssistKind::None),
40 "Re-enable this test",
41 ignore_attr.syntax().text_range(),
42 |builder| {
43 builder.delete(ignore_attr.syntax().text_range());
44 let whitespace = ignore_attr
45 .syntax()
46 .next_sibling_or_token()
47 .and_then(|x| x.into_token())
48 .and_then(ast::Whitespace::cast);
49 if let Some(whitespace) = whitespace {
50 builder.delete(whitespace.syntax().text_range());
51 }
52 },
53 ),
54 }
55}
56
57fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
58 fn_def.attrs().find_map(|attr| {
59 if attr.path()?.syntax().text() == "ignore" {
60 Some(attr)
61 } else {
62 None
63 }
64 })
65}
66
67#[cfg(test)]
68mod tests {
69 use super::ignore_test;
70 use crate::tests::check_assist;
71
72 #[test]
73 fn test_base_case() {
74 check_assist(
75 ignore_test,
76 r#"
77 #[test<|>]
78 fn test() {}
79 "#,
80 r#"
81 #[test]
82 #[ignore]
83 fn test() {}
84 "#,
85 )
86 }
87
88 #[test]
89 fn test_unignore() {
90 check_assist(
91 ignore_test,
92 r#"
93 #[test<|>]
94 #[ignore]
95 fn test() {}
96 "#,
97 r#"
98 #[test]
99 fn test() {}
100 "#,
101 )
102 }
34} 103}
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index f452c98e4..3bd776905 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -44,7 +44,9 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
44 acc.add_resolution(ctx, name.to_string(), &res) 44 acc.add_resolution(ctx, name.to_string(), &res)
45 }); 45 });
46 46
47 fuzzy_completion(acc, ctx).unwrap_or_default() 47 if ctx.config.enable_experimental_completions {
48 fuzzy_completion(acc, ctx).unwrap_or_default()
49 }
48} 50}
49 51
50fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { 52fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index 82874ff25..f50735372 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -9,6 +9,7 @@ use assists::utils::MergeBehaviour;
9#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct CompletionConfig { 10pub struct CompletionConfig {
11 pub enable_postfix_completions: bool, 11 pub enable_postfix_completions: bool,
12 pub enable_experimental_completions: bool,
12 pub add_call_parenthesis: bool, 13 pub add_call_parenthesis: bool,
13 pub add_call_argument_snippets: bool, 14 pub add_call_argument_snippets: bool,
14 pub snippet_cap: Option<SnippetCap>, 15 pub snippet_cap: Option<SnippetCap>,
@@ -30,6 +31,7 @@ impl Default for CompletionConfig {
30 fn default() -> Self { 31 fn default() -> Self {
31 CompletionConfig { 32 CompletionConfig {
32 enable_postfix_completions: true, 33 enable_postfix_completions: true,
34 enable_experimental_completions: true,
33 add_call_parenthesis: true, 35 add_call_parenthesis: true,
34 add_call_argument_snippets: true, 36 add_call_argument_snippets: true,
35 snippet_cap: Some(SnippetCap { _private: () }), 37 snippet_cap: Some(SnippetCap { _private: () }),
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index cb6e0554e..aecc1378b 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -67,6 +67,13 @@ pub use crate::{
67// fn test_name() {} 67// fn test_name() {}
68// } 68// }
69// ``` 69// ```
70//
71// And experimental completions, enabled with the `rust-analyzer.completion.enableExperimental` setting.
72// This flag enables or disables:
73//
74// - Auto import: additional completion options with automatic `use` import and options from all project importable items, matched for the input
75//
76// Experimental completions might cause issues with performance and completion list look.
70 77
71/// Main entry point for completion. We run completion as a two-phase process. 78/// Main entry point for completion. We run completion as a two-phase process.
72/// 79///
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml
index 44499bc79..3d9436d69 100644
--- a/crates/flycheck/Cargo.toml
+++ b/crates/flycheck/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12[dependencies] 12[dependencies]
13crossbeam-channel = "0.5.0" 13crossbeam-channel = "0.5.0"
14log = "0.4.8" 14log = "0.4.8"
15cargo_metadata = "0.12.0" 15cargo_metadata = "=0.12.0"
16serde_json = "1.0.48" 16serde_json = "1.0.48"
17jod-thread = "0.1.1" 17jod-thread = "0.1.1"
18 18
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs
index 07333c453..8c767b249 100644
--- a/crates/hir/src/db.rs
+++ b/crates/hir/src/db.rs
@@ -11,7 +11,7 @@ pub use hir_def::db::{
11}; 11};
12pub use hir_expand::db::{ 12pub use hir_expand::db::{
13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, 13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery,
14 MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroQuery, 14 MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroExpansionQuery,
15}; 15};
16pub use hir_ty::db::*; 16pub use hir_ty::db::*;
17 17
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 5fea25ef1..ed110329d 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -57,8 +57,8 @@ pub use hir_def::{
57 visibility::Visibility, 57 visibility::Visibility,
58}; 58};
59pub use hir_expand::{ 59pub use hir_expand::{
60 name::known, name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, 60 db::MacroResult, name::known, name::AsName, name::Name, HirFileId, InFile, MacroCallId,
61 /* FIXME */ MacroDefId, MacroFile, Origin, 61 MacroCallLoc, /* FIXME */ MacroDefId, MacroFile, Origin,
62}; 62};
63pub use hir_ty::display::HirDisplay; 63pub use hir_ty::display::HirDisplay;
64 64
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index ade57ac1b..a9099eb22 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -13,6 +13,19 @@ use crate::{
13 MacroFile, ProcMacroExpander, 13 MacroFile, ProcMacroExpander,
14}; 14};
15 15
16/// A result of some macro expansion.
17#[derive(Debug, Clone, Eq, PartialEq)]
18pub struct MacroResult<T> {
19 /// The result of the expansion. Might be `None` when error recovery was impossible and no
20 /// usable result was produced.
21 pub value: Option<T>,
22
23 /// The error that occurred during expansion or processing.
24 ///
25 /// Since we do error recovery, getting an error here does not mean that `value` will be absent.
26 pub error: Option<String>,
27}
28
16#[derive(Debug, Clone, Eq, PartialEq)] 29#[derive(Debug, Clone, Eq, PartialEq)]
17pub enum TokenExpander { 30pub enum TokenExpander {
18 MacroRules(mbe::MacroRules), 31 MacroRules(mbe::MacroRules),
@@ -75,9 +88,11 @@ pub trait AstDatabase: SourceDatabase {
75 #[salsa::transparent] 88 #[salsa::transparent]
76 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; 89 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
77 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; 90 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>;
78 fn parse_macro(&self, macro_file: MacroFile) 91 fn parse_macro_expansion(
79 -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>; 92 &self,
80 fn macro_expand(&self, macro_call: MacroCallId) -> (Option<Arc<tt::Subtree>>, Option<String>); 93 macro_file: MacroFile,
94 ) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>;
95 fn macro_expand(&self, macro_call: MacroCallId) -> MacroResult<Arc<tt::Subtree>>;
81 96
82 #[salsa::interned] 97 #[salsa::interned]
83 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; 98 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
@@ -85,6 +100,20 @@ pub trait AstDatabase: SourceDatabase {
85 fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; 100 fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>;
86} 101}
87 102
103impl<T> MacroResult<T> {
104 fn error(message: String) -> Self {
105 Self { value: None, error: Some(message) }
106 }
107
108 fn map<U>(self, f: impl FnOnce(T) -> U) -> MacroResult<U> {
109 MacroResult { value: self.value.map(f), error: self.error }
110 }
111
112 fn drop_value<U>(self) -> MacroResult<U> {
113 MacroResult { value: None, error: self.error }
114 }
115}
116
88/// This expands the given macro call, but with different arguments. This is 117/// This expands the given macro call, but with different arguments. This is
89/// used for completion, where we want to see what 'would happen' if we insert a 118/// used for completion, where we want to see what 'would happen' if we insert a
90/// token. The `token_to_map` mapped down into the expansion, with the mapped 119/// token. The `token_to_map` mapped down into the expansion, with the mapped
@@ -102,23 +131,20 @@ pub fn expand_hypothetical(
102 let token_id = tmap_1.token_by_range(range)?; 131 let token_id = tmap_1.token_by_range(range)?;
103 let macro_def = expander(db, actual_macro_call)?; 132 let macro_def = expander(db, actual_macro_call)?;
104 let (node, tmap_2) = 133 let (node, tmap_2) =
105 parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1))))?; 134 parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1)))).value?;
106 let token_id = macro_def.0.map_id_down(token_id); 135 let token_id = macro_def.0.map_id_down(token_id);
107 let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; 136 let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?;
108 let token = syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?; 137 let token = syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?;
109 Some((node.syntax_node(), token)) 138 Some((node.syntax_node(), token))
110} 139}
111 140
112pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { 141fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
113 let map = 142 let map =
114 db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); 143 db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it));
115 Arc::new(map) 144 Arc::new(map)
116} 145}
117 146
118pub(crate) fn macro_def( 147fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
119 db: &dyn AstDatabase,
120 id: MacroDefId,
121) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
122 match id.kind { 148 match id.kind {
123 MacroDefKind::Declarative => { 149 MacroDefKind::Declarative => {
124 let macro_call = id.ast_id?.to_node(db); 150 let macro_call = id.ast_id?.to_node(db);
@@ -149,7 +175,7 @@ pub(crate) fn macro_def(
149 } 175 }
150} 176}
151 177
152pub(crate) fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { 178fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
153 let id = match id { 179 let id = match id {
154 MacroCallId::LazyMacro(id) => id, 180 MacroCallId::LazyMacro(id) => id,
155 MacroCallId::EagerMacro(_id) => { 181 MacroCallId::EagerMacro(_id) => {
@@ -162,19 +188,13 @@ pub(crate) fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<Gr
162 Some(arg.green().clone()) 188 Some(arg.green().clone())
163} 189}
164 190
165pub(crate) fn macro_arg( 191fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
166 db: &dyn AstDatabase,
167 id: MacroCallId,
168) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
169 let arg = db.macro_arg_text(id)?; 192 let arg = db.macro_arg_text(id)?;
170 let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg))?; 193 let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg))?;
171 Some(Arc::new((tt, tmap))) 194 Some(Arc::new((tt, tmap)))
172} 195}
173 196
174pub(crate) fn macro_expand( 197fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> MacroResult<Arc<tt::Subtree>> {
175 db: &dyn AstDatabase,
176 id: MacroCallId,
177) -> (Option<Arc<tt::Subtree>>, Option<String>) {
178 macro_expand_with_arg(db, id, None) 198 macro_expand_with_arg(db, id, None)
179} 199}
180 200
@@ -195,17 +215,19 @@ fn macro_expand_with_arg(
195 db: &dyn AstDatabase, 215 db: &dyn AstDatabase,
196 id: MacroCallId, 216 id: MacroCallId,
197 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, 217 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
198) -> (Option<Arc<tt::Subtree>>, Option<String>) { 218) -> MacroResult<Arc<tt::Subtree>> {
199 let lazy_id = match id { 219 let lazy_id = match id {
200 MacroCallId::LazyMacro(id) => id, 220 MacroCallId::LazyMacro(id) => id,
201 MacroCallId::EagerMacro(id) => { 221 MacroCallId::EagerMacro(id) => {
202 if arg.is_some() { 222 if arg.is_some() {
203 return ( 223 return MacroResult::error(
204 None, 224 "hypothetical macro expansion not implemented for eager macro".to_owned(),
205 Some("hypothetical macro expansion not implemented for eager macro".to_owned()),
206 ); 225 );
207 } else { 226 } else {
208 return (Some(db.lookup_intern_eager_expansion(id).subtree), None); 227 return MacroResult {
228 value: Some(db.lookup_intern_eager_expansion(id).subtree),
229 error: None,
230 };
209 } 231 }
210 } 232 }
211 }; 233 };
@@ -213,23 +235,24 @@ fn macro_expand_with_arg(
213 let loc = db.lookup_intern_macro(lazy_id); 235 let loc = db.lookup_intern_macro(lazy_id);
214 let macro_arg = match arg.or_else(|| db.macro_arg(id)) { 236 let macro_arg = match arg.or_else(|| db.macro_arg(id)) {
215 Some(it) => it, 237 Some(it) => it,
216 None => return (None, Some("Fail to args in to tt::TokenTree".into())), 238 None => return MacroResult::error("Fail to args in to tt::TokenTree".into()),
217 }; 239 };
218 240
219 let macro_rules = match db.macro_def(loc.def) { 241 let macro_rules = match db.macro_def(loc.def) {
220 Some(it) => it, 242 Some(it) => it,
221 None => return (None, Some("Fail to find macro definition".into())), 243 None => return MacroResult::error("Fail to find macro definition".into()),
222 }; 244 };
223 let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, &macro_arg.0); 245 let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, &macro_arg.0);
224 // Set a hard limit for the expanded tt 246 // Set a hard limit for the expanded tt
225 let count = tt.count(); 247 let count = tt.count();
226 if count > 262144 { 248 if count > 262144 {
227 return (None, Some(format!("Total tokens count exceed limit : count = {}", count))); 249 return MacroResult::error(format!("Total tokens count exceed limit : count = {}", count));
228 } 250 }
229 (Some(Arc::new(tt)), err.map(|e| format!("{:?}", e))) 251
252 MacroResult { value: Some(Arc::new(tt)), error: err.map(|e| format!("{:?}", e)) }
230} 253}
231 254
232pub(crate) fn expand_proc_macro( 255fn expand_proc_macro(
233 db: &dyn AstDatabase, 256 db: &dyn AstDatabase,
234 id: MacroCallId, 257 id: MacroCallId,
235) -> Result<tt::Subtree, mbe::ExpandError> { 258) -> Result<tt::Subtree, mbe::ExpandError> {
@@ -256,36 +279,36 @@ pub(crate) fn expand_proc_macro(
256 expander.expand(db, lazy_id, &macro_arg.0) 279 expander.expand(db, lazy_id, &macro_arg.0)
257} 280}
258 281
259pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { 282fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
260 match file_id.0 { 283 match file_id.0 {
261 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), 284 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
262 HirFileIdRepr::MacroFile(macro_file) => { 285 HirFileIdRepr::MacroFile(macro_file) => {
263 db.parse_macro(macro_file).map(|(it, _)| it.syntax_node()) 286 db.parse_macro_expansion(macro_file).map(|(it, _)| it.syntax_node()).value
264 } 287 }
265 } 288 }
266} 289}
267 290
268pub(crate) fn parse_macro( 291fn parse_macro_expansion(
269 db: &dyn AstDatabase, 292 db: &dyn AstDatabase,
270 macro_file: MacroFile, 293 macro_file: MacroFile,
271) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { 294) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
272 parse_macro_with_arg(db, macro_file, None) 295 parse_macro_with_arg(db, macro_file, None)
273} 296}
274 297
275pub fn parse_macro_with_arg( 298fn parse_macro_with_arg(
276 db: &dyn AstDatabase, 299 db: &dyn AstDatabase,
277 macro_file: MacroFile, 300 macro_file: MacroFile,
278 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, 301 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
279) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { 302) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
280 let _p = profile::span("parse_macro_query"); 303 let _p = profile::span("parse_macro_query");
281 304
282 let macro_call_id = macro_file.macro_call_id; 305 let macro_call_id = macro_file.macro_call_id;
283 let (tt, err) = if let Some(arg) = arg { 306 let result = if let Some(arg) = arg {
284 macro_expand_with_arg(db, macro_call_id, Some(arg)) 307 macro_expand_with_arg(db, macro_call_id, Some(arg))
285 } else { 308 } else {
286 db.macro_expand(macro_call_id) 309 db.macro_expand(macro_call_id)
287 }; 310 };
288 if let Some(err) = &err { 311 if let Some(err) = &result.error {
289 // Note: 312 // Note:
290 // The final goal we would like to make all parse_macro success, 313 // The final goal we would like to make all parse_macro success,
291 // such that the following log will not call anyway. 314 // such that the following log will not call anyway.
@@ -313,30 +336,40 @@ pub fn parse_macro_with_arg(
313 log::warn!("fail on macro_parse: (reason: {})", err); 336 log::warn!("fail on macro_parse: (reason: {})", err);
314 } 337 }
315 } 338 }
339 }
340 let tt = match result.value {
341 Some(tt) => tt,
342 None => return result.drop_value(),
316 }; 343 };
317 let tt = tt?;
318 344
319 let fragment_kind = to_fragment_kind(db, macro_call_id); 345 let fragment_kind = to_fragment_kind(db, macro_call_id);
320 346
321 let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?; 347 let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) {
348 Ok(it) => it,
349 Err(err) => {
350 return MacroResult::error(format!("{:?}", err));
351 }
352 };
322 353
323 if err.is_none() { 354 match result.error {
324 Some((parse, Arc::new(rev_token_map))) 355 Some(error) => {
325 } else { 356 // Safety check for recursive identity macro.
326 // FIXME: 357 let node = parse.syntax_node();
327 // In future, we should propagate the actual error with recovery information 358 let file: HirFileId = macro_file.into();
328 // instead of ignore the error here. 359 let call_node = match file.call_node(db) {
329 360 Some(it) => it,
330 // Safe check for recurisve identity macro 361 None => {
331 let node = parse.syntax_node(); 362 return MacroResult::error(error);
332 let file: HirFileId = macro_file.into(); 363 }
333 let call_node = file.call_node(db)?; 364 };
334 365
335 if !diff(&node, &call_node.value).is_empty() { 366 if !diff(&node, &call_node.value).is_empty() {
336 Some((parse, Arc::new(rev_token_map))) 367 MacroResult { value: Some((parse, Arc::new(rev_token_map))), error: Some(error) }
337 } else { 368 } else {
338 None 369 return MacroResult::error(error);
370 }
339 } 371 }
372 None => MacroResult { value: Some((parse, Arc::new(rev_token_map))), error: None },
340 } 373 }
341} 374}
342 375
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 17f1178ed..83e09738b 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -144,7 +144,7 @@ impl HirFileId {
144 let def_tt = loc.def.ast_id?.to_node(db).token_tree()?; 144 let def_tt = loc.def.ast_id?.to_node(db).token_tree()?;
145 145
146 let macro_def = db.macro_def(loc.def)?; 146 let macro_def = db.macro_def(loc.def)?;
147 let (parse, exp_map) = db.parse_macro(macro_file)?; 147 let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?;
148 let macro_arg = db.macro_arg(macro_file.macro_call_id)?; 148 let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
149 149
150 Some(ExpansionInfo { 150 Some(ExpansionInfo {
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index 5bd03f2ac..62c329731 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -216,14 +216,14 @@
216//! U(P, p) := U(P, (r_1, p_2, .., p_n)) 216//! U(P, p) := U(P, (r_1, p_2, .., p_n))
217//! || U(P, (r_2, p_2, .., p_n)) 217//! || U(P, (r_2, p_2, .., p_n))
218//! ``` 218//! ```
219use std::sync::Arc; 219use std::{iter, sync::Arc};
220 220
221use arena::Idx; 221use arena::Idx;
222use hir_def::{ 222use hir_def::{
223 adt::VariantData, 223 adt::VariantData,
224 body::Body, 224 body::Body,
225 expr::{Expr, Literal, Pat, PatId}, 225 expr::{Expr, Literal, Pat, PatId},
226 AdtId, EnumVariantId, VariantId, 226 AdtId, EnumVariantId, StructId, VariantId,
227}; 227};
228use smallvec::{smallvec, SmallVec}; 228use smallvec::{smallvec, SmallVec};
229 229
@@ -366,16 +366,17 @@ impl PatStack {
366 366
367 let head_pat = head.as_pat(cx); 367 let head_pat = head.as_pat(cx);
368 let result = match (head_pat, constructor) { 368 let result = match (head_pat, constructor) {
369 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => { 369 (Pat::Tuple { args: pat_ids, ellipsis }, &Constructor::Tuple { arity }) => {
370 if ellipsis.is_some() { 370 if let Some(ellipsis) = ellipsis {
371 // If there are ellipsis here, we should add the correct number of 371 let (pre, post) = pat_ids.split_at(ellipsis);
372 // Pat::Wild patterns to `pat_ids`. We should be able to use the 372 let n_wild_pats = arity.saturating_sub(pat_ids.len());
373 // constructors arity for this, but at the time of writing we aren't 373 let pre_iter = pre.iter().map(Into::into);
374 // correctly calculating this arity when ellipsis are present. 374 let wildcards = iter::repeat(PatIdOrWild::Wild).take(n_wild_pats);
375 return Err(MatchCheckErr::NotImplemented); 375 let post_iter = post.iter().map(Into::into);
376 Some(self.replace_head_with(pre_iter.chain(wildcards).chain(post_iter)))
377 } else {
378 Some(self.replace_head_with(pat_ids.iter()))
376 } 379 }
377
378 Some(self.replace_head_with(pat_ids.iter()))
379 } 380 }
380 (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => { 381 (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => {
381 match cx.body.exprs[lit_expr] { 382 match cx.body.exprs[lit_expr] {
@@ -390,21 +391,28 @@ impl PatStack {
390 } 391 }
391 } 392 }
392 (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?), 393 (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?),
393 (Pat::Path(_), Constructor::Enum(constructor)) => { 394 (Pat::Path(_), constructor) => {
394 // unit enum variants become `Pat::Path` 395 // unit enum variants become `Pat::Path`
395 let pat_id = head.as_id().expect("we know this isn't a wild"); 396 let pat_id = head.as_id().expect("we know this isn't a wild");
396 if !enum_variant_matches(cx, pat_id, *constructor) { 397 let variant_id: VariantId = match constructor {
398 &Constructor::Enum(e) => e.into(),
399 &Constructor::Struct(s) => s.into(),
400 _ => return Err(MatchCheckErr::NotImplemented),
401 };
402 if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) {
397 None 403 None
398 } else { 404 } else {
399 Some(self.to_tail()) 405 Some(self.to_tail())
400 } 406 }
401 } 407 }
402 ( 408 (Pat::TupleStruct { args: ref pat_ids, ellipsis, .. }, constructor) => {
403 Pat::TupleStruct { args: ref pat_ids, ellipsis, .. },
404 Constructor::Enum(enum_constructor),
405 ) => {
406 let pat_id = head.as_id().expect("we know this isn't a wild"); 409 let pat_id = head.as_id().expect("we know this isn't a wild");
407 if !enum_variant_matches(cx, pat_id, *enum_constructor) { 410 let variant_id: VariantId = match constructor {
411 &Constructor::Enum(e) => e.into(),
412 &Constructor::Struct(s) => s.into(),
413 _ => return Err(MatchCheckErr::MalformedMatchArm),
414 };
415 if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) {
408 None 416 None
409 } else { 417 } else {
410 let constructor_arity = constructor.arity(cx)?; 418 let constructor_arity = constructor.arity(cx)?;
@@ -442,12 +450,22 @@ impl PatStack {
442 } 450 }
443 } 451 }
444 } 452 }
445 (Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => { 453 (Pat::Record { args: ref arg_patterns, .. }, constructor) => {
446 let pat_id = head.as_id().expect("we know this isn't a wild"); 454 let pat_id = head.as_id().expect("we know this isn't a wild");
447 if !enum_variant_matches(cx, pat_id, *e) { 455 let (variant_id, variant_data) = match constructor {
456 &Constructor::Enum(e) => (
457 e.into(),
458 cx.db.enum_data(e.parent).variants[e.local_id].variant_data.clone(),
459 ),
460 &Constructor::Struct(s) => {
461 (s.into(), cx.db.struct_data(s).variant_data.clone())
462 }
463 _ => return Err(MatchCheckErr::MalformedMatchArm),
464 };
465 if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) {
448 None 466 None
449 } else { 467 } else {
450 match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() { 468 match variant_data.as_ref() {
451 VariantData::Record(struct_field_arena) => { 469 VariantData::Record(struct_field_arena) => {
452 // Here we treat any missing fields in the record as the wild pattern, as 470 // Here we treat any missing fields in the record as the wild pattern, as
453 // if the record has ellipsis. We want to do this here even if the 471 // if the record has ellipsis. We want to do this here even if the
@@ -726,6 +744,7 @@ enum Constructor {
726 Bool(bool), 744 Bool(bool),
727 Tuple { arity: usize }, 745 Tuple { arity: usize },
728 Enum(EnumVariantId), 746 Enum(EnumVariantId),
747 Struct(StructId),
729} 748}
730 749
731impl Constructor { 750impl Constructor {
@@ -740,6 +759,11 @@ impl Constructor {
740 VariantData::Unit => 0, 759 VariantData::Unit => 0,
741 } 760 }
742 } 761 }
762 &Constructor::Struct(s) => match cx.db.struct_data(s).variant_data.as_ref() {
763 VariantData::Tuple(struct_field_data) => struct_field_data.len(),
764 VariantData::Record(struct_field_data) => struct_field_data.len(),
765 VariantData::Unit => 0,
766 },
743 }; 767 };
744 768
745 Ok(arity) 769 Ok(arity)
@@ -748,7 +772,7 @@ impl Constructor {
748 fn all_constructors(&self, cx: &MatchCheckCtx) -> Vec<Constructor> { 772 fn all_constructors(&self, cx: &MatchCheckCtx) -> Vec<Constructor> {
749 match self { 773 match self {
750 Constructor::Bool(_) => vec![Constructor::Bool(true), Constructor::Bool(false)], 774 Constructor::Bool(_) => vec![Constructor::Bool(true), Constructor::Bool(false)],
751 Constructor::Tuple { .. } => vec![*self], 775 Constructor::Tuple { .. } | Constructor::Struct(_) => vec![*self],
752 Constructor::Enum(e) => cx 776 Constructor::Enum(e) => cx
753 .db 777 .db
754 .enum_data(e.parent) 778 .enum_data(e.parent)
@@ -767,10 +791,11 @@ impl Constructor {
767fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> { 791fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> {
768 let res = match pat.as_pat(cx) { 792 let res = match pat.as_pat(cx) {
769 Pat::Wild => None, 793 Pat::Wild => None,
770 // FIXME somehow create the Tuple constructor with the proper arity. If there are 794 Pat::Tuple { .. } => {
771 // ellipsis, the arity is not equal to the number of patterns. 795 let pat_id = pat.as_id().expect("we already know this pattern is not a wild");
772 Pat::Tuple { args: pats, ellipsis } if ellipsis.is_none() => { 796 Some(Constructor::Tuple {
773 Some(Constructor::Tuple { arity: pats.len() }) 797 arity: cx.infer.type_of_pat[pat_id].as_tuple().ok_or(MatchCheckErr::Unknown)?.len(),
798 })
774 } 799 }
775 Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] { 800 Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] {
776 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), 801 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)),
@@ -784,6 +809,7 @@ fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Opt
784 VariantId::EnumVariantId(enum_variant_id) => { 809 VariantId::EnumVariantId(enum_variant_id) => {
785 Some(Constructor::Enum(enum_variant_id)) 810 Some(Constructor::Enum(enum_variant_id))
786 } 811 }
812 VariantId::StructId(struct_id) => Some(Constructor::Struct(struct_id)),
787 _ => return Err(MatchCheckErr::NotImplemented), 813 _ => return Err(MatchCheckErr::NotImplemented),
788 } 814 }
789 } 815 }
@@ -828,13 +854,13 @@ fn all_constructors_covered(
828 854
829 false 855 false
830 }), 856 }),
857 &Constructor::Struct(s) => used_constructors.iter().any(|constructor| match constructor {
858 &Constructor::Struct(sid) => sid == s,
859 _ => false,
860 }),
831 } 861 }
832} 862}
833 863
834fn enum_variant_matches(cx: &MatchCheckCtx, pat_id: PatId, enum_variant_id: EnumVariantId) -> bool {
835 Some(enum_variant_id.into()) == cx.infer.variant_resolution_for_pat(pat_id)
836}
837
838#[cfg(test)] 864#[cfg(test)]
839mod tests { 865mod tests {
840 use crate::diagnostics::tests::check_diagnostics; 866 use crate::diagnostics::tests::check_diagnostics;
@@ -846,8 +872,8 @@ mod tests {
846fn main() { 872fn main() {
847 match () { } 873 match () { }
848 //^^ Missing match arm 874 //^^ Missing match arm
849 match (()) { } 875 match (()) { }
850 //^^^^ Missing match arm 876 //^^^^ Missing match arm
851 877
852 match () { _ => (), } 878 match () { _ => (), }
853 match () { () => (), } 879 match () { () => (), }
@@ -1352,6 +1378,123 @@ fn main() {
1352 ); 1378 );
1353 } 1379 }
1354 1380
1381 #[test]
1382 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
1383 check_diagnostics(
1384 r#"
1385fn main() {
1386 match (false, true, false) {
1387 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
1388 (false, ..) => (),
1389 }
1390}"#,
1391 );
1392 }
1393
1394 #[test]
1395 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
1396 check_diagnostics(
1397 r#"
1398fn main() {
1399 match (false, true, false) {
1400 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
1401 (.., false) => (),
1402 }
1403}"#,
1404 );
1405 }
1406
1407 #[test]
1408 fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() {
1409 check_diagnostics(
1410 r#"
1411fn main() {
1412 match (false, true, false) {
1413 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
1414 (true, .., false) => (),
1415 }
1416}"#,
1417 );
1418 }
1419
1420 #[test]
1421 fn record_struct() {
1422 check_diagnostics(
1423 r#"struct Foo { a: bool }
1424fn main(f: Foo) {
1425 match f {}
1426 //^ Missing match arm
1427 match f { Foo { a: true } => () }
1428 //^ Missing match arm
1429 match &f { Foo { a: true } => () }
1430 //^^ Missing match arm
1431 match f { Foo { a: _ } => () }
1432 match f {
1433 Foo { a: true } => (),
1434 Foo { a: false } => (),
1435 }
1436 match &f {
1437 Foo { a: true } => (),
1438 Foo { a: false } => (),
1439 }
1440}
1441"#,
1442 );
1443 }
1444
1445 #[test]
1446 fn tuple_struct() {
1447 check_diagnostics(
1448 r#"struct Foo(bool);
1449fn main(f: Foo) {
1450 match f {}
1451 //^ Missing match arm
1452 match f { Foo(true) => () }
1453 //^ Missing match arm
1454 match f {
1455 Foo(true) => (),
1456 Foo(false) => (),
1457 }
1458}
1459"#,
1460 );
1461 }
1462
1463 #[test]
1464 fn unit_struct() {
1465 check_diagnostics(
1466 r#"struct Foo;
1467fn main(f: Foo) {
1468 match f {}
1469 //^ Missing match arm
1470 match f { Foo => () }
1471}
1472"#,
1473 );
1474 }
1475
1476 #[test]
1477 fn record_struct_ellipsis() {
1478 check_diagnostics(
1479 r#"struct Foo { foo: bool, bar: bool }
1480fn main(f: Foo) {
1481 match f { Foo { foo: true, .. } => () }
1482 //^ Missing match arm
1483 match f {
1484 //^ Missing match arm
1485 Foo { foo: true, .. } => (),
1486 Foo { bar: false, .. } => ()
1487 }
1488 match f { Foo { .. } => () }
1489 match f {
1490 Foo { foo: true, .. } => (),
1491 Foo { foo: false, .. } => ()
1492 }
1493}
1494"#,
1495 );
1496 }
1497
1355 mod false_negatives { 1498 mod false_negatives {
1356 //! The implementation of match checking here is a work in progress. As we roll this out, we 1499 //! The implementation of match checking here is a work in progress. As we roll this out, we
1357 //! prefer false negatives to false positives (ideally there would be no false positives). This 1500 //! prefer false negatives to false positives (ideally there would be no false positives). This
@@ -1393,46 +1536,5 @@ fn main() {
1393"#, 1536"#,
1394 ); 1537 );
1395 } 1538 }
1396
1397 #[test]
1398 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
1399 // We don't currently handle tuple patterns with ellipsis.
1400 check_diagnostics(
1401 r#"
1402fn main() {
1403 match (false, true, false) {
1404 (false, ..) => (),
1405 }
1406}
1407"#,
1408 );
1409 }
1410
1411 #[test]
1412 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
1413 // We don't currently handle tuple patterns with ellipsis.
1414 check_diagnostics(
1415 r#"
1416fn main() {
1417 match (false, true, false) {
1418 (.., false) => (),
1419 }
1420}
1421"#,
1422 );
1423 }
1424
1425 #[test]
1426 fn struct_missing_arm() {
1427 // We don't currently handle structs.
1428 check_diagnostics(
1429 r#"
1430struct Foo { a: bool }
1431fn main(f: Foo) {
1432 match f { Foo { a: true } => () }
1433}
1434"#,
1435 );
1436 }
1437 } 1539 }
1438} 1540}
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index cde2ab82b..b70ec55eb 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -23,6 +23,7 @@ impl<'a> InferenceContext<'a> {
23 expected: &Ty, 23 expected: &Ty,
24 default_bm: BindingMode, 24 default_bm: BindingMode,
25 id: PatId, 25 id: PatId,
26 ellipsis: Option<usize>,
26 ) -> Ty { 27 ) -> Ty {
27 let (ty, def) = self.resolve_variant(path); 28 let (ty, def) = self.resolve_variant(path);
28 let var_data = def.map(|it| variant_data(self.db.upcast(), it)); 29 let var_data = def.map(|it| variant_data(self.db.upcast(), it));
@@ -34,8 +35,15 @@ impl<'a> InferenceContext<'a> {
34 let substs = ty.substs().unwrap_or_else(Substs::empty); 35 let substs = ty.substs().unwrap_or_else(Substs::empty);
35 36
36 let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); 37 let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
38 let (pre, post) = match ellipsis {
39 Some(idx) => subpats.split_at(idx),
40 None => (&subpats[..], &[][..]),
41 };
42 let post_idx_offset = field_tys.iter().count() - post.len();
37 43
38 for (i, &subpat) in subpats.iter().enumerate() { 44 let pre_iter = pre.iter().enumerate();
45 let post_iter = (post_idx_offset..).zip(post.iter());
46 for (i, &subpat) in pre_iter.chain(post_iter) {
39 let expected_ty = var_data 47 let expected_ty = var_data
40 .as_ref() 48 .as_ref()
41 .and_then(|d| d.field(&Name::new_tuple_field(i))) 49 .and_then(|d| d.field(&Name::new_tuple_field(i)))
@@ -111,20 +119,29 @@ impl<'a> InferenceContext<'a> {
111 let expected = expected; 119 let expected = expected;
112 120
113 let ty = match &body[pat] { 121 let ty = match &body[pat] {
114 Pat::Tuple { ref args, .. } => { 122 &Pat::Tuple { ref args, ellipsis } => {
115 let expectations = match expected.as_tuple() { 123 let expectations = match expected.as_tuple() {
116 Some(parameters) => &*parameters.0, 124 Some(parameters) => &*parameters.0,
117 _ => &[], 125 _ => &[],
118 }; 126 };
119 let expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown));
120 127
121 let inner_tys = args 128 let (pre, post) = match ellipsis {
122 .iter() 129 Some(idx) => args.split_at(idx),
123 .zip(expectations_iter) 130 None => (&args[..], &[][..]),
124 .map(|(&pat, ty)| self.infer_pat(pat, ty, default_bm)) 131 };
125 .collect(); 132 let n_uncovered_patterns = expectations.len().saturating_sub(args.len());
133 let mut expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown));
134 let mut infer_pat = |(&pat, ty)| self.infer_pat(pat, ty, default_bm);
135
136 let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + args.len());
137 inner_tys.extend(pre.iter().zip(expectations_iter.by_ref()).map(&mut infer_pat));
138 inner_tys.extend(expectations_iter.by_ref().take(n_uncovered_patterns).cloned());
139 inner_tys.extend(post.iter().zip(expectations_iter).map(infer_pat));
126 140
127 Ty::apply(TypeCtor::Tuple { cardinality: args.len() as u16 }, Substs(inner_tys)) 141 Ty::apply(
142 TypeCtor::Tuple { cardinality: inner_tys.len() as u16 },
143 Substs(inner_tys.into()),
144 )
128 } 145 }
129 Pat::Or(ref pats) => { 146 Pat::Or(ref pats) => {
130 if let Some((first_pat, rest)) = pats.split_first() { 147 if let Some((first_pat, rest)) = pats.split_first() {
@@ -150,9 +167,14 @@ impl<'a> InferenceContext<'a> {
150 let subty = self.infer_pat(*pat, expectation, default_bm); 167 let subty = self.infer_pat(*pat, expectation, default_bm);
151 Ty::apply_one(TypeCtor::Ref(*mutability), subty) 168 Ty::apply_one(TypeCtor::Ref(*mutability), subty)
152 } 169 }
153 Pat::TupleStruct { path: p, args: subpats, .. } => { 170 Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
154 self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat) 171 p.as_ref(),
155 } 172 subpats,
173 expected,
174 default_bm,
175 pat,
176 *ellipsis,
177 ),
156 Pat::Record { path: p, args: fields, ellipsis: _ } => { 178 Pat::Record { path: p, args: fields, ellipsis: _ } => {
157 self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat) 179 self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat)
158 } 180 }
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 6a965ac4f..5a5f48fd0 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -679,3 +679,98 @@ fn box_pattern() {
679 "#]], 679 "#]],
680 ); 680 );
681} 681}
682
683#[test]
684fn tuple_ellipsis_pattern() {
685 check_infer(
686 r#"
687fn foo(tuple: (u8, i16, f32)) {
688 match tuple {
689 (.., b, c) => {},
690 (a, .., c) => {},
691 (a, b, ..) => {},
692 (a, b) => {/*too short*/}
693 (a, b, c, d) => {/*too long*/}
694 _ => {}
695 }
696}"#,
697 expect![[r#"
698 7..12 'tuple': (u8, i16, f32)
699 30..224 '{ ... } }': ()
700 36..222 'match ... }': ()
701 42..47 'tuple': (u8, i16, f32)
702 58..68 '(.., b, c)': (u8, i16, f32)
703 63..64 'b': i16
704 66..67 'c': f32
705 72..74 '{}': ()
706 84..94 '(a, .., c)': (u8, i16, f32)
707 85..86 'a': u8
708 92..93 'c': f32
709 98..100 '{}': ()
710 110..120 '(a, b, ..)': (u8, i16, f32)
711 111..112 'a': u8
712 114..115 'b': i16
713 124..126 '{}': ()
714 136..142 '(a, b)': (u8, i16, f32)
715 137..138 'a': u8
716 140..141 'b': i16
717 146..161 '{/*too short*/}': ()
718 170..182 '(a, b, c, d)': (u8, i16, f32, {unknown})
719 171..172 'a': u8
720 174..175 'b': i16
721 177..178 'c': f32
722 180..181 'd': {unknown}
723 186..200 '{/*too long*/}': ()
724 209..210 '_': (u8, i16, f32)
725 214..216 '{}': ()
726 "#]],
727 );
728}
729
730#[test]
731fn tuple_struct_ellipsis_pattern() {
732 check_infer(
733 r#"
734struct Tuple(u8, i16, f32);
735fn foo(tuple: Tuple) {
736 match tuple {
737 Tuple(.., b, c) => {},
738 Tuple(a, .., c) => {},
739 Tuple(a, b, ..) => {},
740 Tuple(a, b) => {/*too short*/}
741 Tuple(a, b, c, d) => {/*too long*/}
742 _ => {}
743 }
744}"#,
745 expect![[r#"
746 35..40 'tuple': Tuple
747 49..268 '{ ... } }': ()
748 55..266 'match ... }': ()
749 61..66 'tuple': Tuple
750 77..92 'Tuple(.., b, c)': Tuple
751 87..88 'b': i16
752 90..91 'c': f32
753 96..98 '{}': ()
754 108..123 'Tuple(a, .., c)': Tuple
755 114..115 'a': u8
756 121..122 'c': f32
757 127..129 '{}': ()
758 139..154 'Tuple(a, b, ..)': Tuple
759 145..146 'a': u8
760 148..149 'b': i16
761 158..160 '{}': ()
762 170..181 'Tuple(a, b)': Tuple
763 176..177 'a': u8
764 179..180 'b': i16
765 185..200 '{/*too short*/}': ()
766 209..226 'Tuple(... c, d)': Tuple
767 215..216 'a': u8
768 218..219 'b': i16
769 221..222 'c': f32
770 224..225 'd': {unknown}
771 230..244 '{/*too long*/}': ()
772 253..254 '_': Tuple
773 258..260 '{}': ()
774 "#]],
775 );
776}
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs
index 8e91c99d7..b75f88ed9 100644
--- a/crates/ide/src/status.rs
+++ b/crates/ide/src/status.rs
@@ -1,6 +1,6 @@
1use std::{fmt, iter::FromIterator, sync::Arc}; 1use std::{fmt, iter::FromIterator, sync::Arc};
2 2
3use hir::MacroFile; 3use hir::{MacroFile, MacroResult};
4use ide_db::base_db::{ 4use ide_db::base_db::{
5 salsa::debug::{DebugQueryTable, TableEntry}, 5 salsa::debug::{DebugQueryTable, TableEntry},
6 CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId, 6 CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId,
@@ -19,7 +19,7 @@ fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
19 ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>() 19 ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>()
20} 20}
21fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { 21fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
22 hir::db::ParseMacroQuery.in_db(db).entries::<SyntaxTreeStats>() 22 hir::db::ParseMacroExpansionQuery.in_db(db).entries::<SyntaxTreeStats>()
23} 23}
24 24
25// Feature: Status 25// Feature: Status
@@ -115,10 +115,12 @@ impl FromIterator<TableEntry<FileId, Parse<ast::SourceFile>>> for SyntaxTreeStat
115 } 115 }
116} 116}
117 117
118impl<M> FromIterator<TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>> for SyntaxTreeStats { 118impl<M> FromIterator<TableEntry<MacroFile, MacroResult<(Parse<SyntaxNode>, M)>>>
119 for SyntaxTreeStats
120{
119 fn from_iter<T>(iter: T) -> SyntaxTreeStats 121 fn from_iter<T>(iter: T) -> SyntaxTreeStats
120 where 122 where
121 T: IntoIterator<Item = TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>>, 123 T: IntoIterator<Item = TableEntry<MacroFile, MacroResult<(Parse<SyntaxNode>, M)>>>,
122 { 124 {
123 let mut res = SyntaxTreeStats::default(); 125 let mut res = SyntaxTreeStats::default();
124 for entry in iter { 126 for entry in iter {
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index da16fa21d..987191fe3 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -76,7 +76,7 @@ impl RootDatabase {
76 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); 76 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
77 77
78 base_db::ParseQuery.in_db(self).sweep(sweep); 78 base_db::ParseQuery.in_db(self).sweep(sweep);
79 hir::db::ParseMacroQuery.in_db(self).sweep(sweep); 79 hir::db::ParseMacroExpansionQuery.in_db(self).sweep(sweep);
80 80
81 // Macros do take significant space, but less then the syntax trees 81 // Macros do take significant space, but less then the syntax trees
82 // self.query(hir::db::MacroDefQuery).sweep(sweep); 82 // self.query(hir::db::MacroDefQuery).sweep(sweep);
@@ -143,7 +143,7 @@ impl RootDatabase {
143 hir::db::AstIdMapQuery 143 hir::db::AstIdMapQuery
144 hir::db::MacroArgTextQuery 144 hir::db::MacroArgTextQuery
145 hir::db::MacroDefQuery 145 hir::db::MacroDefQuery
146 hir::db::ParseMacroQuery 146 hir::db::ParseMacroExpansionQuery
147 hir::db::MacroExpandQuery 147 hir::db::MacroExpandQuery
148 148
149 // DefDatabase 149 // DefDatabase
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 38ebdbf79..05139a651 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -113,7 +113,7 @@ impl RootDatabase {
113 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) { 113 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
114 let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP); 114 let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP);
115 base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity); 115 base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
116 hir::db::ParseMacroQuery.in_db_mut(self).set_lru_capacity(lru_capacity); 116 hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
117 hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity); 117 hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
118 } 118 }
119} 119}
diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml
index 048b32186..729372968 100644
--- a/crates/proc_macro_srv/Cargo.toml
+++ b/crates/proc_macro_srv/Cargo.toml
@@ -20,7 +20,7 @@ proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" }
20test_utils = { path = "../test_utils", version = "0.0.0" } 20test_utils = { path = "../test_utils", version = "0.0.0" }
21 21
22[dev-dependencies] 22[dev-dependencies]
23cargo_metadata = "0.12.0" 23cargo_metadata = "=0.12.0"
24difference = "2.0.0" 24difference = "2.0.0"
25 25
26# used as proc macro test targets 26# used as proc macro test targets
diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml
index 2d53bcbcc..e0c591603 100644
--- a/crates/project_model/Cargo.toml
+++ b/crates/project_model/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12[dependencies] 12[dependencies]
13log = "0.4.8" 13log = "0.4.8"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15cargo_metadata = "0.12.0" 15cargo_metadata = "=0.12.0"
16serde = { version = "1.0.106", features = ["derive"] } 16serde = { version = "1.0.106", features = ["derive"] }
17serde_json = "1.0.48" 17serde_json = "1.0.48"
18anyhow = "1.0.26" 18anyhow = "1.0.26"
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 56c51486f..436f5041b 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -21,7 +21,7 @@ env_logger = { version = "0.8.1", default-features = false }
21itertools = "0.9.0" 21itertools = "0.9.0"
22jod-thread = "0.1.0" 22jod-thread = "0.1.0"
23log = "0.4.8" 23log = "0.4.8"
24lsp-types = { version = "0.83.1", features = ["proposed"] } 24lsp-types = { version = "0.84.0", features = ["proposed"] }
25parking_lot = "0.11.0" 25parking_lot = "0.11.0"
26pico-args = "0.3.1" 26pico-args = "0.3.1"
27oorandom = "11.1.2" 27oorandom = "11.1.2"
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 5fc6800cf..a334cdb11 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -184,6 +184,7 @@ impl Config {
184 }, 184 },
185 completion: CompletionConfig { 185 completion: CompletionConfig {
186 enable_postfix_completions: true, 186 enable_postfix_completions: true,
187 enable_experimental_completions: true,
187 add_call_parenthesis: true, 188 add_call_parenthesis: true,
188 add_call_argument_snippets: true, 189 add_call_argument_snippets: true,
189 ..CompletionConfig::default() 190 ..CompletionConfig::default()
@@ -306,6 +307,7 @@ impl Config {
306 }; 307 };
307 308
308 self.completion.enable_postfix_completions = data.completion_postfix_enable; 309 self.completion.enable_postfix_completions = data.completion_postfix_enable;
310 self.completion.enable_experimental_completions = data.completion_enableExperimental;
309 self.completion.add_call_parenthesis = data.completion_addCallParenthesis; 311 self.completion.add_call_parenthesis = data.completion_addCallParenthesis;
310 self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets; 312 self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets;
311 self.completion.merge = self.assist.insert_use.merge; 313 self.completion.merge = self.assist.insert_use.merge;
@@ -506,6 +508,7 @@ config_data! {
506 completion_addCallArgumentSnippets: bool = true, 508 completion_addCallArgumentSnippets: bool = true,
507 completion_addCallParenthesis: bool = true, 509 completion_addCallParenthesis: bool = true,
508 completion_postfix_enable: bool = true, 510 completion_postfix_enable: bool = true,
511 completion_enableExperimental: bool = true,
509 512
510 diagnostics_enable: bool = true, 513 diagnostics_enable: bool = true,
511 diagnostics_enableExperimental: bool = true, 514 diagnostics_enableExperimental: bool = true,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 6ea08adce..b34ff092d 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -503,7 +503,13 @@ impl GlobalState {
503 })? 503 })?
504 .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| { 504 .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| {
505 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) { 505 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
506 let doc = this.mem_docs.get_mut(&path).unwrap(); 506 let doc = match this.mem_docs.get_mut(&path) {
507 Some(doc) => doc,
508 None => {
509 log::error!("expected DidChangeTextDocument: {}", path);
510 return Ok(());
511 }
512 };
507 let vfs = &mut this.vfs.write().0; 513 let vfs = &mut this.vfs.write().0;
508 let file_id = vfs.file_id(&path).unwrap(); 514 let file_id = vfs.file_id(&path).unwrap();
509 let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap(); 515 let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap();
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 2f35425bb..2052b800c 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -629,12 +629,21 @@ pub(crate) fn resource_op(
629 match file_system_edit { 629 match file_system_edit {
630 FileSystemEdit::CreateFile { anchor, dst } => { 630 FileSystemEdit::CreateFile { anchor, dst } => {
631 let uri = snap.anchored_path(anchor, &dst); 631 let uri = snap.anchored_path(anchor, &dst);
632 lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) 632 lsp_types::ResourceOp::Create(lsp_types::CreateFile {
633 uri,
634 options: None,
635 annotation: None,
636 })
633 } 637 }
634 FileSystemEdit::MoveFile { src, anchor, dst } => { 638 FileSystemEdit::MoveFile { src, anchor, dst } => {
635 let old_uri = snap.file_id_to_url(src); 639 let old_uri = snap.file_id_to_url(src);
636 let new_uri = snap.anchored_path(anchor, &dst); 640 let new_uri = snap.anchored_path(anchor, &dst);
637 lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) 641 lsp_types::ResourceOp::Rename(lsp_types::RenameFile {
642 old_uri,
643 new_uri,
644 options: None,
645 annotation: None,
646 })
638 } 647 }
639 } 648 }
640} 649}
@@ -684,9 +693,11 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
684 edits: edit 693 edits: edit
685 .edits 694 .edits
686 .into_iter() 695 .into_iter()
687 .map(|edit| lsp_types::TextEdit { 696 .map(|edit| {
688 range: edit.range, 697 lsp_types::OneOf::Left(lsp_types::TextEdit {
689 new_text: edit.new_text, 698 range: edit.range,
699 new_text: edit.new_text,
700 })
690 }) 701 })
691 .collect(), 702 .collect(),
692 }, 703 },
diff --git a/editors/code/package.json b/editors/code/package.json
index a2d6b1148..c3f1a0d8d 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -460,6 +460,11 @@
460 "default": true, 460 "default": true,
461 "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc." 461 "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc."
462 }, 462 },
463 "rust-analyzer.completion.enableExperimental": {
464 "type": "boolean",
465 "default": true,
466 "markdownDescription": "Display additional completions with potential false positives and performance issues"
467 },
463 "rust-analyzer.callInfo.full": { 468 "rust-analyzer.callInfo.full": {
464 "type": "boolean", 469 "type": "boolean",
465 "default": true, 470 "default": true,
@@ -952,9 +957,6 @@
952 { 957 {
953 "language": "rust", 958 "language": "rust",
954 "scopes": { 959 "scopes": {
955 "macro": [
956 "entity.name.function.macro.rust"
957 ],
958 "attribute": [ 960 "attribute": [
959 "meta.attribute.rust" 961 "meta.attribute.rust"
960 ], 962 ],