aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock45
-rw-r--r--crates/ra_assists/src/assist_context.rs10
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs2
-rw-r--r--crates/ra_assists/src/tests.rs9
-rw-r--r--crates/ra_hir_def/src/lang_item.rs12
-rw-r--r--crates/ra_hir_ty/Cargo.toml6
-rw-r--r--crates/ra_hir_ty/src/db.rs7
-rw-r--r--crates/ra_hir_ty/src/lib.rs8
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs152
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs899
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/interner.rs353
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/mapping.rs701
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/tls.rs83
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs4
-rw-r--r--crates/ra_ide/src/diagnostics.rs103
-rw-r--r--crates/ra_ide/src/display.rs6
-rw-r--r--crates/ra_ide/src/hover.rs17
-rw-r--r--crates/ra_ide/src/join_lines.rs24
-rw-r--r--crates/ra_ide/src/lib.rs31
-rw-r--r--crates/ra_ide/src/references/rename.rs18
-rw-r--r--crates/ra_ide/src/test_utils.rs25
-rw-r--r--crates/ra_ide/src/typing.rs35
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs26
-rw-r--r--crates/ra_ide_db/src/lib.rs1
-rw-r--r--crates/ra_ide_db/src/line_index_utils.rs302
-rw-r--r--crates/ra_ide_db/src/source_change.rs98
-rw-r--r--crates/ra_text_edit/src/lib.rs45
-rw-r--r--crates/rust-analyzer/src/bin/main.rs17
-rw-r--r--crates/rust-analyzer/src/caps.rs70
-rw-r--r--crates/rust-analyzer/src/config.rs15
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap1
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap1
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs1
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs22
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs100
-rw-r--r--crates/rust-analyzer/src/to_proto.rs85
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs70
-rw-r--r--docs/dev/lsp-extensions.md152
-rw-r--r--editors/code/package.json8
-rw-r--r--editors/code/src/client.ts41
-rw-r--r--editors/code/src/commands/index.ts89
-rw-r--r--editors/code/src/commands/join_lines.ts12
-rw-r--r--editors/code/src/commands/on_enter.ts5
-rw-r--r--editors/code/src/commands/ssr.ts6
-rw-r--r--editors/code/src/main.ts4
-rw-r--r--editors/code/src/rust-analyzer-api.ts8
46 files changed, 2030 insertions, 1699 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c06236692..007f05b4d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -114,7 +114,7 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
114[[package]] 114[[package]]
115name = "chalk-derive" 115name = "chalk-derive"
116version = "0.10.1-dev" 116version = "0.10.1-dev"
117source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" 117source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
118dependencies = [ 118dependencies = [
119 "proc-macro2", 119 "proc-macro2",
120 "quote", 120 "quote",
@@ -125,7 +125,7 @@ dependencies = [
125[[package]] 125[[package]]
126name = "chalk-engine" 126name = "chalk-engine"
127version = "0.10.1-dev" 127version = "0.10.1-dev"
128source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" 128source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
129dependencies = [ 129dependencies = [
130 "chalk-macros", 130 "chalk-macros",
131 "rustc-hash", 131 "rustc-hash",
@@ -134,7 +134,7 @@ dependencies = [
134[[package]] 134[[package]]
135name = "chalk-ir" 135name = "chalk-ir"
136version = "0.10.1-dev" 136version = "0.10.1-dev"
137source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" 137source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
138dependencies = [ 138dependencies = [
139 "chalk-derive", 139 "chalk-derive",
140 "chalk-engine", 140 "chalk-engine",
@@ -144,7 +144,7 @@ dependencies = [
144[[package]] 144[[package]]
145name = "chalk-macros" 145name = "chalk-macros"
146version = "0.10.1-dev" 146version = "0.10.1-dev"
147source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" 147source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
148dependencies = [ 148dependencies = [
149 "lazy_static", 149 "lazy_static",
150] 150]
@@ -152,7 +152,7 @@ dependencies = [
152[[package]] 152[[package]]
153name = "chalk-rust-ir" 153name = "chalk-rust-ir"
154version = "0.10.1-dev" 154version = "0.10.1-dev"
155source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" 155source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
156dependencies = [ 156dependencies = [
157 "chalk-derive", 157 "chalk-derive",
158 "chalk-engine", 158 "chalk-engine",
@@ -163,14 +163,14 @@ dependencies = [
163[[package]] 163[[package]]
164name = "chalk-solve" 164name = "chalk-solve"
165version = "0.10.1-dev" 165version = "0.10.1-dev"
166source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" 166source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
167dependencies = [ 167dependencies = [
168 "chalk-derive", 168 "chalk-derive",
169 "chalk-engine", 169 "chalk-engine",
170 "chalk-ir", 170 "chalk-ir",
171 "chalk-macros", 171 "chalk-macros",
172 "chalk-rust-ir", 172 "chalk-rust-ir",
173 "ena 0.13.1", 173 "ena",
174 "itertools", 174 "itertools",
175 "petgraph", 175 "petgraph",
176 "rustc-hash", 176 "rustc-hash",
@@ -309,15 +309,6 @@ checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
309 309
310[[package]] 310[[package]]
311name = "ena" 311name = "ena"
312version = "0.13.1"
313source = "registry+https://github.com/rust-lang/crates.io-index"
314checksum = "8944dc8fa28ce4a38f778bd46bf7d923fe73eed5a439398507246c8e017e6f36"
315dependencies = [
316 "log",
317]
318
319[[package]]
320name = "ena"
321version = "0.14.0" 312version = "0.14.0"
322source = "registry+https://github.com/rust-lang/crates.io-index" 313source = "registry+https://github.com/rust-lang/crates.io-index"
323checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" 314checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3"
@@ -354,9 +345,9 @@ dependencies = [
354 345
355[[package]] 346[[package]]
356name = "fixedbitset" 347name = "fixedbitset"
357version = "0.1.9" 348version = "0.2.0"
358source = "registry+https://github.com/rust-lang/crates.io-index" 349source = "registry+https://github.com/rust-lang/crates.io-index"
359checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" 350checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
360 351
361[[package]] 352[[package]]
362name = "fnv" 353name = "fnv"
@@ -658,9 +649,9 @@ dependencies = [
658 649
659[[package]] 650[[package]]
660name = "lsp-server" 651name = "lsp-server"
661version = "0.3.1" 652version = "0.3.2"
662source = "registry+https://github.com/rust-lang/crates.io-index" 653source = "registry+https://github.com/rust-lang/crates.io-index"
663checksum = "5383e043329615624bbf45e1ba27bd75c176762b2592855c659bc28ac580a06b" 654checksum = "dccec31bfd027ac0dd288a78e19005fd89624d9099456e284b5241316a6c3072"
664dependencies = [ 655dependencies = [
665 "crossbeam-channel", 656 "crossbeam-channel",
666 "log", 657 "log",
@@ -814,12 +805,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
814checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" 805checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
815 806
816[[package]] 807[[package]]
817name = "ordermap"
818version = "0.3.5"
819source = "registry+https://github.com/rust-lang/crates.io-index"
820checksum = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063"
821
822[[package]]
823name = "parking_lot" 808name = "parking_lot"
824version = "0.10.2" 809version = "0.10.2"
825source = "registry+https://github.com/rust-lang/crates.io-index" 810source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -873,12 +858,12 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
873 858
874[[package]] 859[[package]]
875name = "petgraph" 860name = "petgraph"
876version = "0.4.13" 861version = "0.5.0"
877source = "registry+https://github.com/rust-lang/crates.io-index" 862source = "registry+https://github.com/rust-lang/crates.io-index"
878checksum = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" 863checksum = "29c127eea4a29ec6c85d153c59dc1213f33ec74cead30fe4730aecc88cc1fd92"
879dependencies = [ 864dependencies = [
880 "fixedbitset", 865 "fixedbitset",
881 "ordermap", 866 "indexmap",
882] 867]
883 868
884[[package]] 869[[package]]
@@ -1053,7 +1038,7 @@ dependencies = [
1053 "chalk-ir", 1038 "chalk-ir",
1054 "chalk-rust-ir", 1039 "chalk-rust-ir",
1055 "chalk-solve", 1040 "chalk-solve",
1056 "ena 0.14.0", 1041 "ena",
1057 "insta", 1042 "insta",
1058 "itertools", 1043 "itertools",
1059 "log", 1044 "log",
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index f3af70a3e..5b1a4680b 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -5,7 +5,7 @@ use hir::Semantics;
5use ra_db::{FileId, FileRange}; 5use ra_db::{FileId, FileRange};
6use ra_fmt::{leading_indent, reindent}; 6use ra_fmt::{leading_indent, reindent};
7use ra_ide_db::{ 7use ra_ide_db::{
8 source_change::{SingleFileChange, SourceChange}, 8 source_change::{SourceChange, SourceFileEdit},
9 RootDatabase, 9 RootDatabase,
10}; 10};
11use ra_syntax::{ 11use ra_syntax::{
@@ -150,11 +150,10 @@ impl Assists {
150 self.add_impl(label, f) 150 self.add_impl(label, f)
151 } 151 }
152 fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { 152 fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
153 let change_label = label.label.clone();
154 let source_change = if self.resolve { 153 let source_change = if self.resolve {
155 let mut builder = AssistBuilder::new(self.file); 154 let mut builder = AssistBuilder::new(self.file);
156 f(&mut builder); 155 f(&mut builder);
157 Some(builder.finish(change_label)) 156 Some(builder.finish())
158 } else { 157 } else {
159 None 158 None
160 }; 159 };
@@ -246,9 +245,10 @@ impl AssistBuilder {
246 &mut self.edit 245 &mut self.edit
247 } 246 }
248 247
249 fn finish(self, change_label: String) -> SourceChange { 248 fn finish(self) -> SourceChange {
250 let edit = self.edit.finish(); 249 let edit = self.edit.finish();
251 let mut res = SingleFileChange { label: change_label, edit }.into_source_change(self.file); 250 let source_file_edit = SourceFileEdit { file_id: self.file, edit };
251 let mut res: SourceChange = source_file_edit.into();
252 if self.is_snippet { 252 if self.is_snippet {
253 res.is_snippet = true; 253 res.is_snippet = true;
254 } 254 }
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs
index 30229edc2..897da2832 100644
--- a/crates/ra_assists/src/handlers/reorder_fields.rs
+++ b/crates/ra_assists/src/handlers/reorder_fields.rs
@@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists};
23// ``` 23// ```
24// 24//
25pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 25pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 reorder::<ast::RecordLit>(acc, ctx.clone()).or_else(|| reorder::<ast::RecordPat>(acc, ctx)) 26 reorder::<ast::RecordLit>(acc, ctx).or_else(|| reorder::<ast::RecordPat>(acc, ctx))
27} 27}
28 28
29fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 29fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs
index 373a7f7cc..62dd3547f 100644
--- a/crates/ra_assists/src/tests.rs
+++ b/crates/ra_assists/src/tests.rs
@@ -7,8 +7,7 @@ use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
7use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; 7use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
8use ra_syntax::TextRange; 8use ra_syntax::TextRange;
9use test_utils::{ 9use test_utils::{
10 add_cursor, assert_eq_text, extract_offset, extract_range, extract_range_or_offset, 10 assert_eq_text, extract_offset, extract_range, extract_range_or_offset, RangeOrOffset,
11 RangeOrOffset,
12}; 11};
13 12
14use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists}; 13use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists};
@@ -103,12 +102,6 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) {
103 102
104 let mut actual = db.file_text(change.file_id).as_ref().to_owned(); 103 let mut actual = db.file_text(change.file_id).as_ref().to_owned();
105 change.edit.apply(&mut actual); 104 change.edit.apply(&mut actual);
106
107 if !source_change.is_snippet {
108 if let Some(off) = source_change.cursor_position {
109 actual = add_cursor(&actual, off.offset)
110 }
111 }
112 assert_eq_text!(after, &actual); 105 assert_eq_text!(after, &actual);
113 } 106 }
114 (Some(assist), ExpectedResult::Target(target)) => { 107 (Some(assist), ExpectedResult::Target(target)) => {
diff --git a/crates/ra_hir_def/src/lang_item.rs b/crates/ra_hir_def/src/lang_item.rs
index d96ac8c0a..d962db3cc 100644
--- a/crates/ra_hir_def/src/lang_item.rs
+++ b/crates/ra_hir_def/src/lang_item.rs
@@ -73,8 +73,8 @@ pub struct LangItems {
73} 73}
74 74
75impl LangItems { 75impl LangItems {
76 pub fn target<'a>(&'a self, item: &str) -> Option<&'a LangItemTarget> { 76 pub fn target(&self, item: &str) -> Option<LangItemTarget> {
77 self.items.get(item) 77 self.items.get(item).copied()
78 } 78 }
79 79
80 /// Salsa query. This will look for lang items in a specific crate. 80 /// Salsa query. This will look for lang items in a specific crate.
@@ -163,9 +163,13 @@ impl LangItems {
163 ) where 163 ) where
164 T: Into<AttrDefId> + Copy, 164 T: Into<AttrDefId> + Copy,
165 { 165 {
166 let attrs = db.attrs(item.into()); 166 if let Some(lang_item_name) = lang_attr(db, item) {
167 if let Some(lang_item_name) = attrs.by_key("lang").string_value() {
168 self.items.entry(lang_item_name.clone()).or_insert_with(|| constructor(item)); 167 self.items.entry(lang_item_name.clone()).or_insert_with(|| constructor(item));
169 } 168 }
170 } 169 }
171} 170}
171
172pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<SmolStr> {
173 let attrs = db.attrs(item.into());
174 attrs.by_key("lang").string_value().cloned()
175}
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 5fc0ec5e3..b2de7fa34 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -27,9 +27,9 @@ test_utils = { path = "../test_utils" }
27 27
28scoped-tls = "1" 28scoped-tls = "1"
29 29
30chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" } 30chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" }
31chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" } 31chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" }
32chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" } 32chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" }
33 33
34[dev-dependencies] 34[dev-dependencies]
35insta = "0.16.0" 35insta = "0.16.0"
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs
index fdb49560b..0a8bb24ac 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -76,6 +76,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
76 #[salsa::interned] 76 #[salsa::interned]
77 fn intern_type_ctor(&self, type_ctor: TypeCtor) -> crate::TypeCtorId; 77 fn intern_type_ctor(&self, type_ctor: TypeCtor) -> crate::TypeCtorId;
78 #[salsa::interned] 78 #[salsa::interned]
79 fn intern_callable_def(&self, callable_def: CallableDef) -> crate::CallableDefId;
80 #[salsa::interned]
79 fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId; 81 fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId;
80 #[salsa::interned] 82 #[salsa::interned]
81 fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId; 83 fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId;
@@ -89,11 +91,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
89 fn trait_datum(&self, krate: CrateId, trait_id: chalk::TraitId) -> Arc<chalk::TraitDatum>; 91 fn trait_datum(&self, krate: CrateId, trait_id: chalk::TraitId) -> Arc<chalk::TraitDatum>;
90 92
91 #[salsa::invoke(chalk::struct_datum_query)] 93 #[salsa::invoke(chalk::struct_datum_query)]
92 fn struct_datum(&self, krate: CrateId, struct_id: chalk::StructId) -> Arc<chalk::StructDatum>; 94 fn struct_datum(&self, krate: CrateId, struct_id: chalk::AdtId) -> Arc<chalk::StructDatum>;
93 95
94 #[salsa::invoke(crate::traits::chalk::impl_datum_query)] 96 #[salsa::invoke(crate::traits::chalk::impl_datum_query)]
95 fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc<chalk::ImplDatum>; 97 fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc<chalk::ImplDatum>;
96 98
99 #[salsa::invoke(crate::traits::chalk::fn_def_datum_query)]
100 fn fn_def_datum(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> Arc<chalk::FnDefDatum>;
101
97 #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)] 102 #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)]
98 fn associated_ty_value( 103 fn associated_ty_value(
99 &self, 104 &self,
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index c87ee06ce..93cb45a64 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -155,10 +155,16 @@ pub enum TypeCtor {
155/// This exists just for Chalk, because Chalk just has a single `StructId` where 155/// This exists just for Chalk, because Chalk just has a single `StructId` where
156/// we have different kinds of ADTs, primitive types and special type 156/// we have different kinds of ADTs, primitive types and special type
157/// constructors like tuples and function pointers. 157/// constructors like tuples and function pointers.
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
159pub struct TypeCtorId(salsa::InternId); 159pub struct TypeCtorId(salsa::InternId);
160impl_intern_key!(TypeCtorId); 160impl_intern_key!(TypeCtorId);
161 161
162/// This exists just for Chalk, because Chalk just has a single `FnDefId` where
163/// we have different IDs for struct and enum variant constructors.
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
165pub struct CallableDefId(salsa::InternId);
166impl_intern_key!(CallableDefId);
167
162impl TypeCtor { 168impl TypeCtor {
163 pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize { 169 pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize {
164 match self { 170 match self {
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 34f4b9039..0419bc751 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -2602,3 +2602,155 @@ fn test(x: &dyn Foo) {
2602 "### 2602 "###
2603 ); 2603 );
2604} 2604}
2605
2606#[test]
2607fn builtin_copy() {
2608 assert_snapshot!(
2609 infer_with_mismatches(r#"
2610#[lang = "copy"]
2611trait Copy {}
2612
2613struct IsCopy;
2614impl Copy for IsCopy {}
2615struct NotCopy;
2616
2617trait Test { fn test(&self) -> bool; }
2618impl<T: Copy> Test for T {}
2619
2620fn test() {
2621 IsCopy.test();
2622 NotCopy.test();
2623 (IsCopy, IsCopy).test();
2624 (IsCopy, NotCopy).test();
2625}
2626"#, true),
2627 @r###"
2628 111..115 'self': &Self
2629 167..268 '{ ...t(); }': ()
2630 173..179 'IsCopy': IsCopy
2631 173..186 'IsCopy.test()': bool
2632 192..199 'NotCopy': NotCopy
2633 192..206 'NotCopy.test()': {unknown}
2634 212..228 '(IsCop...sCopy)': (IsCopy, IsCopy)
2635 212..235 '(IsCop...test()': bool
2636 213..219 'IsCopy': IsCopy
2637 221..227 'IsCopy': IsCopy
2638 241..258 '(IsCop...tCopy)': (IsCopy, NotCopy)
2639 241..265 '(IsCop...test()': {unknown}
2640 242..248 'IsCopy': IsCopy
2641 250..257 'NotCopy': NotCopy
2642 "###
2643 );
2644}
2645
2646#[test]
2647fn builtin_fn_def_copy() {
2648 assert_snapshot!(
2649 infer_with_mismatches(r#"
2650#[lang = "copy"]
2651trait Copy {}
2652
2653fn foo() {}
2654fn bar<T: Copy>(T) -> T {}
2655struct Struct(usize);
2656enum Enum { Variant(usize) }
2657
2658trait Test { fn test(&self) -> bool; }
2659impl<T: Copy> Test for T {}
2660
2661fn test() {
2662 foo.test();
2663 bar.test();
2664 Struct.test();
2665 Enum::Variant.test();
2666}
2667"#, true),
2668 // wrong result, because the built-in Copy impl for fn defs doesn't exist in Chalk yet
2669 @r###"
2670 42..44 '{}': ()
2671 61..62 'T': {unknown}
2672 69..71 '{}': ()
2673 69..71: expected T, got ()
2674 146..150 'self': &Self
2675 202..282 '{ ...t(); }': ()
2676 208..211 'foo': fn foo()
2677 208..218 'foo.test()': {unknown}
2678 224..227 'bar': fn bar<{unknown}>({unknown}) -> {unknown}
2679 224..234 'bar.test()': {unknown}
2680 240..246 'Struct': Struct(usize) -> Struct
2681 240..253 'Struct.test()': {unknown}
2682 259..272 'Enum::Variant': Variant(usize) -> Enum
2683 259..279 'Enum::...test()': {unknown}
2684 "###
2685 );
2686}
2687
2688#[test]
2689fn builtin_fn_ptr_copy() {
2690 assert_snapshot!(
2691 infer_with_mismatches(r#"
2692#[lang = "copy"]
2693trait Copy {}
2694
2695trait Test { fn test(&self) -> bool; }
2696impl<T: Copy> Test for T {}
2697
2698fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) {
2699 f1.test();
2700 f2.test();
2701 f3.test();
2702}
2703"#, true),
2704 @r###"
2705 55..59 'self': &Self
2706 109..111 'f1': fn()
2707 119..121 'f2': fn(usize) -> u8
2708 140..142 'f3': fn(u8, u8) -> &u8
2709 163..211 '{ ...t(); }': ()
2710 169..171 'f1': fn()
2711 169..178 'f1.test()': bool
2712 184..186 'f2': fn(usize) -> u8
2713 184..193 'f2.test()': bool
2714 199..201 'f3': fn(u8, u8) -> &u8
2715 199..208 'f3.test()': bool
2716 "###
2717 );
2718}
2719
2720#[test]
2721fn builtin_sized() {
2722 assert_snapshot!(
2723 infer_with_mismatches(r#"
2724#[lang = "sized"]
2725trait Sized {}
2726
2727trait Test { fn test(&self) -> bool; }
2728impl<T: Sized> Test for T {}
2729
2730fn test() {
2731 1u8.test();
2732 (*"foo").test(); // not Sized
2733 (1u8, 1u8).test();
2734 (1u8, *"foo").test(); // not Sized
2735}
2736"#, true),
2737 @r###"
2738 57..61 'self': &Self
2739 114..229 '{ ...ized }': ()
2740 120..123 '1u8': u8
2741 120..130 '1u8.test()': bool
2742 136..151 '(*"foo").test()': {unknown}
2743 137..143 '*"foo"': str
2744 138..143 '"foo"': &str
2745 170..180 '(1u8, 1u8)': (u8, u8)
2746 170..187 '(1u8, ...test()': bool
2747 171..174 '1u8': u8
2748 176..179 '1u8': u8
2749 193..206 '(1u8, *"foo")': (u8, str)
2750 193..213 '(1u8, ...test()': {unknown}
2751 194..197 '1u8': u8
2752 199..205 '*"foo"': str
2753 200..205 '"foo"': &str
2754 "###
2755 );
2756}
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index 5870618a0..5b0f12a3c 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -1,301 +1,29 @@
1//! Conversion code from/to Chalk. 1//! Conversion code from/to Chalk.
2use std::{fmt, sync::Arc}; 2use std::sync::Arc;
3 3
4use log::debug; 4use log::debug;
5 5
6use chalk_ir::{ 6use chalk_ir::{fold::shift::Shift, GenericArg, TypeName};
7 cast::Cast, fold::shift::Shift, interner::HasInterner, Goal, GoalData, Parameter,
8 PlaceholderIndex, TypeName, UniverseIndex,
9};
10 7
11use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId}; 8use hir_def::{
12use ra_db::{ 9 lang_item::{lang_attr, LangItemTarget},
13 salsa::{InternId, InternKey}, 10 AssocContainerId, AssocItemId, HasModule, Lookup, TypeAliasId,
14 CrateId,
15}; 11};
12use ra_db::{salsa::InternKey, CrateId};
16 13
17use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation}; 14use super::{builtin, AssocTyValue, ChalkContext, Impl};
18use crate::{ 15use crate::{
19 db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics, 16 db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics,
20 ApplicationTy, DebruijnIndex, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, 17 CallableDef, DebruijnIndex, GenericPredicate, Substs, Ty, TypeCtor,
21}; 18};
19use chalk_rust_ir::WellKnownTrait;
20use mapping::{convert_where_clauses, generic_predicate_to_inline_bound, make_binders};
22 21
23pub(super) mod tls; 22pub use self::interner::*;
24
25#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
26pub struct Interner;
27
28impl chalk_ir::interner::Interner for Interner {
29 type InternedType = Box<chalk_ir::TyData<Self>>;
30 type InternedLifetime = chalk_ir::LifetimeData<Self>;
31 type InternedParameter = chalk_ir::ParameterData<Self>;
32 type InternedGoal = Arc<GoalData<Self>>;
33 type InternedGoals = Vec<Goal<Self>>;
34 type InternedSubstitution = Vec<Parameter<Self>>;
35 type InternedProgramClause = chalk_ir::ProgramClauseData<Self>;
36 type InternedProgramClauses = Arc<[chalk_ir::ProgramClause<Self>]>;
37 type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>;
38 type InternedParameterKinds = Vec<chalk_ir::ParameterKind<()>>;
39 type InternedCanonicalVarKinds = Vec<chalk_ir::ParameterKind<UniverseIndex>>;
40 type Identifier = TypeAliasId;
41 type DefId = InternId;
42
43 fn debug_struct_id(
44 type_kind_id: StructId,
45 fmt: &mut fmt::Formatter<'_>,
46 ) -> Option<fmt::Result> {
47 tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt)))
48 }
49
50 fn debug_trait_id(type_kind_id: TraitId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
51 tls::with_current_program(|prog| Some(prog?.debug_trait_id(type_kind_id, fmt)))
52 }
53
54 fn debug_assoc_type_id(id: AssocTypeId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
55 tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt)))
56 }
57
58 fn debug_alias(
59 alias: &chalk_ir::AliasTy<Interner>,
60 fmt: &mut fmt::Formatter<'_>,
61 ) -> Option<fmt::Result> {
62 tls::with_current_program(|prog| Some(prog?.debug_alias(alias, fmt)))
63 }
64
65 fn debug_projection_ty(
66 proj: &chalk_ir::ProjectionTy<Interner>,
67 fmt: &mut fmt::Formatter<'_>,
68 ) -> Option<fmt::Result> {
69 tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt)))
70 }
71
72 fn debug_opaque_ty(
73 opaque_ty: &chalk_ir::OpaqueTy<Interner>,
74 fmt: &mut fmt::Formatter<'_>,
75 ) -> Option<fmt::Result> {
76 tls::with_current_program(|prog| Some(prog?.debug_opaque_ty(opaque_ty, fmt)))
77 }
78
79 fn debug_opaque_ty_id(
80 opaque_ty_id: chalk_ir::OpaqueTyId<Self>,
81 fmt: &mut fmt::Formatter<'_>,
82 ) -> Option<fmt::Result> {
83 tls::with_current_program(|prog| Some(prog?.debug_opaque_ty_id(opaque_ty_id, fmt)))
84 }
85
86 fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
87 tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt)))
88 }
89
90 fn debug_lifetime(
91 lifetime: &chalk_ir::Lifetime<Interner>,
92 fmt: &mut fmt::Formatter<'_>,
93 ) -> Option<fmt::Result> {
94 tls::with_current_program(|prog| Some(prog?.debug_lifetime(lifetime, fmt)))
95 }
96
97 fn debug_parameter(
98 parameter: &Parameter<Interner>,
99 fmt: &mut fmt::Formatter<'_>,
100 ) -> Option<fmt::Result> {
101 tls::with_current_program(|prog| Some(prog?.debug_parameter(parameter, fmt)))
102 }
103
104 fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
105 tls::with_current_program(|prog| Some(prog?.debug_goal(goal, fmt)))
106 }
107
108 fn debug_goals(
109 goals: &chalk_ir::Goals<Interner>,
110 fmt: &mut fmt::Formatter<'_>,
111 ) -> Option<fmt::Result> {
112 tls::with_current_program(|prog| Some(prog?.debug_goals(goals, fmt)))
113 }
114
115 fn debug_program_clause_implication(
116 pci: &chalk_ir::ProgramClauseImplication<Interner>,
117 fmt: &mut fmt::Formatter<'_>,
118 ) -> Option<fmt::Result> {
119 tls::with_current_program(|prog| Some(prog?.debug_program_clause_implication(pci, fmt)))
120 }
121
122 fn debug_application_ty(
123 application_ty: &chalk_ir::ApplicationTy<Interner>,
124 fmt: &mut fmt::Formatter<'_>,
125 ) -> Option<fmt::Result> {
126 tls::with_current_program(|prog| Some(prog?.debug_application_ty(application_ty, fmt)))
127 }
128
129 fn debug_substitution(
130 substitution: &chalk_ir::Substitution<Interner>,
131 fmt: &mut fmt::Formatter<'_>,
132 ) -> Option<fmt::Result> {
133 tls::with_current_program(|prog| Some(prog?.debug_substitution(substitution, fmt)))
134 }
135
136 fn debug_separator_trait_ref(
137 separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>,
138 fmt: &mut fmt::Formatter<'_>,
139 ) -> Option<fmt::Result> {
140 tls::with_current_program(|prog| {
141 Some(prog?.debug_separator_trait_ref(separator_trait_ref, fmt))
142 })
143 }
144
145 fn intern_ty(&self, ty: chalk_ir::TyData<Self>) -> Box<chalk_ir::TyData<Self>> {
146 Box::new(ty)
147 }
148
149 fn ty_data<'a>(&self, ty: &'a Box<chalk_ir::TyData<Self>>) -> &'a chalk_ir::TyData<Self> {
150 ty
151 }
152
153 fn intern_lifetime(
154 &self,
155 lifetime: chalk_ir::LifetimeData<Self>,
156 ) -> chalk_ir::LifetimeData<Self> {
157 lifetime
158 }
159
160 fn lifetime_data<'a>(
161 &self,
162 lifetime: &'a chalk_ir::LifetimeData<Self>,
163 ) -> &'a chalk_ir::LifetimeData<Self> {
164 lifetime
165 }
166
167 fn intern_parameter(
168 &self,
169 parameter: chalk_ir::ParameterData<Self>,
170 ) -> chalk_ir::ParameterData<Self> {
171 parameter
172 }
173
174 fn parameter_data<'a>(
175 &self,
176 parameter: &'a chalk_ir::ParameterData<Self>,
177 ) -> &'a chalk_ir::ParameterData<Self> {
178 parameter
179 }
180
181 fn intern_goal(&self, goal: GoalData<Self>) -> Arc<GoalData<Self>> {
182 Arc::new(goal)
183 }
184
185 fn intern_goals<E>(
186 &self,
187 data: impl IntoIterator<Item = Result<Goal<Self>, E>>,
188 ) -> Result<Self::InternedGoals, E> {
189 data.into_iter().collect()
190 }
191
192 fn goal_data<'a>(&self, goal: &'a Arc<GoalData<Self>>) -> &'a GoalData<Self> {
193 goal
194 }
195
196 fn goals_data<'a>(&self, goals: &'a Vec<Goal<Interner>>) -> &'a [Goal<Interner>] {
197 goals
198 }
199
200 fn intern_substitution<E>(
201 &self,
202 data: impl IntoIterator<Item = Result<Parameter<Self>, E>>,
203 ) -> Result<Vec<Parameter<Self>>, E> {
204 data.into_iter().collect()
205 }
206
207 fn substitution_data<'a>(
208 &self,
209 substitution: &'a Vec<Parameter<Self>>,
210 ) -> &'a [Parameter<Self>] {
211 substitution
212 }
213
214 fn intern_program_clause(
215 &self,
216 data: chalk_ir::ProgramClauseData<Self>,
217 ) -> chalk_ir::ProgramClauseData<Self> {
218 data
219 }
220
221 fn program_clause_data<'a>(
222 &self,
223 clause: &'a chalk_ir::ProgramClauseData<Self>,
224 ) -> &'a chalk_ir::ProgramClauseData<Self> {
225 clause
226 }
227
228 fn intern_program_clauses<E>(
229 &self,
230 data: impl IntoIterator<Item = Result<chalk_ir::ProgramClause<Self>, E>>,
231 ) -> Result<Arc<[chalk_ir::ProgramClause<Self>]>, E> {
232 data.into_iter().collect()
233 }
234
235 fn program_clauses_data<'a>(
236 &self,
237 clauses: &'a Arc<[chalk_ir::ProgramClause<Self>]>,
238 ) -> &'a [chalk_ir::ProgramClause<Self>] {
239 &clauses
240 }
241
242 fn intern_quantified_where_clauses<E>(
243 &self,
244 data: impl IntoIterator<Item = Result<chalk_ir::QuantifiedWhereClause<Self>, E>>,
245 ) -> Result<Self::InternedQuantifiedWhereClauses, E> {
246 data.into_iter().collect()
247 }
248
249 fn quantified_where_clauses_data<'a>(
250 &self,
251 clauses: &'a Self::InternedQuantifiedWhereClauses,
252 ) -> &'a [chalk_ir::QuantifiedWhereClause<Self>] {
253 clauses
254 }
255
256 fn intern_parameter_kinds<E>(
257 &self,
258 data: impl IntoIterator<Item = Result<chalk_ir::ParameterKind<()>, E>>,
259 ) -> Result<Self::InternedParameterKinds, E> {
260 data.into_iter().collect()
261 }
262 23
263 fn parameter_kinds_data<'a>( 24pub(super) mod tls;
264 &self, 25mod interner;
265 parameter_kinds: &'a Self::InternedParameterKinds, 26mod mapping;
266 ) -> &'a [chalk_ir::ParameterKind<()>] {
267 &parameter_kinds
268 }
269
270 fn intern_canonical_var_kinds<E>(
271 &self,
272 data: impl IntoIterator<Item = Result<chalk_ir::ParameterKind<UniverseIndex>, E>>,
273 ) -> Result<Self::InternedCanonicalVarKinds, E> {
274 data.into_iter().collect()
275 }
276
277 fn canonical_var_kinds_data<'a>(
278 &self,
279 canonical_var_kinds: &'a Self::InternedCanonicalVarKinds,
280 ) -> &'a [chalk_ir::ParameterKind<UniverseIndex>] {
281 &canonical_var_kinds
282 }
283}
284
285impl chalk_ir::interner::HasInterner for Interner {
286 type Interner = Self;
287}
288
289pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
290pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum<Interner>;
291pub type TraitId = chalk_ir::TraitId<Interner>;
292pub type TraitDatum = chalk_rust_ir::TraitDatum<Interner>;
293pub type StructId = chalk_ir::StructId<Interner>;
294pub type StructDatum = chalk_rust_ir::StructDatum<Interner>;
295pub type ImplId = chalk_ir::ImplId<Interner>;
296pub type ImplDatum = chalk_rust_ir::ImplDatum<Interner>;
297pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId<Interner>;
298pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue<Interner>;
299 27
300pub(super) trait ToChalk { 28pub(super) trait ToChalk {
301 type Chalk; 29 type Chalk;
@@ -310,508 +38,6 @@ where
310 T::from_chalk(db, chalk) 38 T::from_chalk(db, chalk)
311} 39}
312 40
313impl ToChalk for Ty {
314 type Chalk = chalk_ir::Ty<Interner>;
315 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty<Interner> {
316 match self {
317 Ty::Apply(apply_ty) => {
318 let name = apply_ty.ctor.to_chalk(db);
319 let substitution = apply_ty.parameters.to_chalk(db);
320 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
321 }
322 Ty::Projection(proj_ty) => {
323 let associated_ty_id = proj_ty.associated_ty.to_chalk(db);
324 let substitution = proj_ty.parameters.to_chalk(db);
325 chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
326 associated_ty_id,
327 substitution,
328 })
329 .cast(&Interner)
330 .intern(&Interner)
331 }
332 Ty::Placeholder(id) => {
333 let interned_id = db.intern_type_param_id(id);
334 PlaceholderIndex {
335 ui: UniverseIndex::ROOT,
336 idx: interned_id.as_intern_id().as_usize(),
337 }
338 .to_ty::<Interner>(&Interner)
339 }
340 Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx).intern(&Interner),
341 Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"),
342 Ty::Dyn(predicates) => {
343 let where_clauses = chalk_ir::QuantifiedWhereClauses::from(
344 &Interner,
345 predicates.iter().filter(|p| !p.is_error()).cloned().map(|p| p.to_chalk(db)),
346 );
347 let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) };
348 chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner)
349 }
350 Ty::Opaque(_) | Ty::Unknown => {
351 let substitution = chalk_ir::Substitution::empty(&Interner);
352 let name = TypeName::Error;
353 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
354 }
355 }
356 }
357 fn from_chalk(db: &dyn HirDatabase, chalk: chalk_ir::Ty<Interner>) -> Self {
358 match chalk.data(&Interner).clone() {
359 chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name {
360 TypeName::Error => Ty::Unknown,
361 _ => {
362 let ctor = from_chalk(db, apply_ty.name);
363 let parameters = from_chalk(db, apply_ty.substitution);
364 Ty::Apply(ApplicationTy { ctor, parameters })
365 }
366 },
367 chalk_ir::TyData::Placeholder(idx) => {
368 assert_eq!(idx.ui, UniverseIndex::ROOT);
369 let interned_id = crate::db::GlobalTypeParamId::from_intern_id(
370 crate::salsa::InternId::from(idx.idx),
371 );
372 Ty::Placeholder(db.lookup_intern_type_param_id(interned_id))
373 }
374 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => {
375 let associated_ty = from_chalk(db, proj.associated_ty_id);
376 let parameters = from_chalk(db, proj.substitution);
377 Ty::Projection(ProjectionTy { associated_ty, parameters })
378 }
379 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(),
380 chalk_ir::TyData::Function(_) => unimplemented!(),
381 chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx),
382 chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown,
383 chalk_ir::TyData::Dyn(where_clauses) => {
384 assert_eq!(where_clauses.bounds.binders.len(&Interner), 1);
385 let predicates = where_clauses
386 .bounds
387 .skip_binders()
388 .iter(&Interner)
389 .map(|c| from_chalk(db, c.clone()))
390 .collect();
391 Ty::Dyn(predicates)
392 }
393 }
394 }
395}
396
397impl ToChalk for Substs {
398 type Chalk = chalk_ir::Substitution<Interner>;
399
400 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Substitution<Interner> {
401 chalk_ir::Substitution::from(&Interner, self.iter().map(|ty| ty.clone().to_chalk(db)))
402 }
403
404 fn from_chalk(db: &dyn HirDatabase, parameters: chalk_ir::Substitution<Interner>) -> Substs {
405 let tys = parameters
406 .iter(&Interner)
407 .map(|p| match p.ty(&Interner) {
408 Some(ty) => from_chalk(db, ty.clone()),
409 None => unimplemented!(),
410 })
411 .collect();
412 Substs(tys)
413 }
414}
415
416impl ToChalk for TraitRef {
417 type Chalk = chalk_ir::TraitRef<Interner>;
418
419 fn to_chalk(self: TraitRef, db: &dyn HirDatabase) -> chalk_ir::TraitRef<Interner> {
420 let trait_id = self.trait_.to_chalk(db);
421 let substitution = self.substs.to_chalk(db);
422 chalk_ir::TraitRef { trait_id, substitution }
423 }
424
425 fn from_chalk(db: &dyn HirDatabase, trait_ref: chalk_ir::TraitRef<Interner>) -> Self {
426 let trait_ = from_chalk(db, trait_ref.trait_id);
427 let substs = from_chalk(db, trait_ref.substitution);
428 TraitRef { trait_, substs }
429 }
430}
431
432impl ToChalk for hir_def::TraitId {
433 type Chalk = TraitId;
434
435 fn to_chalk(self, _db: &dyn HirDatabase) -> TraitId {
436 chalk_ir::TraitId(self.as_intern_id())
437 }
438
439 fn from_chalk(_db: &dyn HirDatabase, trait_id: TraitId) -> hir_def::TraitId {
440 InternKey::from_intern_id(trait_id.0)
441 }
442}
443
444impl ToChalk for TypeCtor {
445 type Chalk = TypeName<Interner>;
446
447 fn to_chalk(self, db: &dyn HirDatabase) -> TypeName<Interner> {
448 match self {
449 TypeCtor::AssociatedType(type_alias) => {
450 let type_id = type_alias.to_chalk(db);
451 TypeName::AssociatedType(type_id)
452 }
453 _ => {
454 // other TypeCtors get interned and turned into a chalk StructId
455 let struct_id = db.intern_type_ctor(self).into();
456 TypeName::Struct(struct_id)
457 }
458 }
459 }
460
461 fn from_chalk(db: &dyn HirDatabase, type_name: TypeName<Interner>) -> TypeCtor {
462 match type_name {
463 TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()),
464 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
465 TypeName::OpaqueType(_) => unreachable!(),
466
467 TypeName::Scalar(_) => unreachable!(),
468 TypeName::Tuple(_) => unreachable!(),
469 TypeName::Raw(_) => unreachable!(),
470 TypeName::Slice => unreachable!(),
471 TypeName::Ref(_) => unreachable!(),
472 TypeName::Str => unreachable!(),
473
474 TypeName::Error => {
475 // this should not be reached, since we don't represent TypeName::Error with TypeCtor
476 unreachable!()
477 }
478 }
479 }
480}
481
482impl ToChalk for Impl {
483 type Chalk = ImplId;
484
485 fn to_chalk(self, db: &dyn HirDatabase) -> ImplId {
486 db.intern_chalk_impl(self).into()
487 }
488
489 fn from_chalk(db: &dyn HirDatabase, impl_id: ImplId) -> Impl {
490 db.lookup_intern_chalk_impl(impl_id.into())
491 }
492}
493
494impl ToChalk for TypeAliasId {
495 type Chalk = AssocTypeId;
496
497 fn to_chalk(self, _db: &dyn HirDatabase) -> AssocTypeId {
498 chalk_ir::AssocTypeId(self.as_intern_id())
499 }
500
501 fn from_chalk(_db: &dyn HirDatabase, type_alias_id: AssocTypeId) -> TypeAliasId {
502 InternKey::from_intern_id(type_alias_id.0)
503 }
504}
505
506impl ToChalk for AssocTyValue {
507 type Chalk = AssociatedTyValueId;
508
509 fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValueId {
510 db.intern_assoc_ty_value(self).into()
511 }
512
513 fn from_chalk(db: &dyn HirDatabase, assoc_ty_value_id: AssociatedTyValueId) -> AssocTyValue {
514 db.lookup_intern_assoc_ty_value(assoc_ty_value_id.into())
515 }
516}
517
518impl ToChalk for GenericPredicate {
519 type Chalk = chalk_ir::QuantifiedWhereClause<Interner>;
520
521 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::QuantifiedWhereClause<Interner> {
522 match self {
523 GenericPredicate::Implemented(trait_ref) => {
524 let chalk_trait_ref = trait_ref.to_chalk(db);
525 let chalk_trait_ref = chalk_trait_ref.shifted_in(&Interner);
526 make_binders(chalk_ir::WhereClause::Implemented(chalk_trait_ref), 0)
527 }
528 GenericPredicate::Projection(projection_pred) => {
529 let ty = projection_pred.ty.to_chalk(db).shifted_in(&Interner);
530 let projection = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner);
531 let alias = chalk_ir::AliasTy::Projection(projection);
532 make_binders(chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), 0)
533 }
534 GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"),
535 }
536 }
537
538 fn from_chalk(
539 db: &dyn HirDatabase,
540 where_clause: chalk_ir::QuantifiedWhereClause<Interner>,
541 ) -> GenericPredicate {
542 // we don't produce any where clauses with binders and can't currently deal with them
543 match where_clause
544 .skip_binders()
545 .shifted_out(&Interner)
546 .expect("unexpected bound vars in where clause")
547 {
548 chalk_ir::WhereClause::Implemented(tr) => {
549 GenericPredicate::Implemented(from_chalk(db, tr))
550 }
551 chalk_ir::WhereClause::AliasEq(projection_eq) => {
552 let projection_ty = from_chalk(
553 db,
554 match projection_eq.alias {
555 chalk_ir::AliasTy::Projection(p) => p,
556 _ => unimplemented!(),
557 },
558 );
559 let ty = from_chalk(db, projection_eq.ty);
560 GenericPredicate::Projection(super::ProjectionPredicate { projection_ty, ty })
561 }
562 }
563 }
564}
565
566impl ToChalk for ProjectionTy {
567 type Chalk = chalk_ir::ProjectionTy<Interner>;
568
569 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy<Interner> {
570 chalk_ir::ProjectionTy {
571 associated_ty_id: self.associated_ty.to_chalk(db),
572 substitution: self.parameters.to_chalk(db),
573 }
574 }
575
576 fn from_chalk(
577 db: &dyn HirDatabase,
578 projection_ty: chalk_ir::ProjectionTy<Interner>,
579 ) -> ProjectionTy {
580 ProjectionTy {
581 associated_ty: from_chalk(db, projection_ty.associated_ty_id),
582 parameters: from_chalk(db, projection_ty.substitution),
583 }
584 }
585}
586
587impl ToChalk for super::ProjectionPredicate {
588 type Chalk = chalk_ir::AliasEq<Interner>;
589
590 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> {
591 chalk_ir::AliasEq {
592 alias: chalk_ir::AliasTy::Projection(self.projection_ty.to_chalk(db)),
593 ty: self.ty.to_chalk(db),
594 }
595 }
596
597 fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq<Interner>) -> Self {
598 unimplemented!()
599 }
600}
601
602impl ToChalk for Obligation {
603 type Chalk = chalk_ir::DomainGoal<Interner>;
604
605 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::DomainGoal<Interner> {
606 match self {
607 Obligation::Trait(tr) => tr.to_chalk(db).cast(&Interner),
608 Obligation::Projection(pr) => pr.to_chalk(db).cast(&Interner),
609 }
610 }
611
612 fn from_chalk(_db: &dyn HirDatabase, _goal: chalk_ir::DomainGoal<Interner>) -> Self {
613 unimplemented!()
614 }
615}
616
617impl<T> ToChalk for Canonical<T>
618where
619 T: ToChalk,
620 T::Chalk: HasInterner<Interner = Interner>,
621{
622 type Chalk = chalk_ir::Canonical<T::Chalk>;
623
624 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> {
625 let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT);
626 let value = self.value.to_chalk(db);
627 chalk_ir::Canonical {
628 value,
629 binders: chalk_ir::CanonicalVarKinds::from(&Interner, vec![parameter; self.num_vars]),
630 }
631 }
632
633 fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> {
634 Canonical {
635 num_vars: canonical.binders.len(&Interner),
636 value: from_chalk(db, canonical.value),
637 }
638 }
639}
640
641impl ToChalk for Arc<super::TraitEnvironment> {
642 type Chalk = chalk_ir::Environment<Interner>;
643
644 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Environment<Interner> {
645 let mut clauses = Vec::new();
646 for pred in &self.predicates {
647 if pred.is_error() {
648 // for env, we just ignore errors
649 continue;
650 }
651 let program_clause: chalk_ir::ProgramClause<Interner> =
652 pred.clone().to_chalk(db).cast(&Interner);
653 clauses.push(program_clause.into_from_env_clause(&Interner));
654 }
655 chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses)
656 }
657
658 fn from_chalk(
659 _db: &dyn HirDatabase,
660 _env: chalk_ir::Environment<Interner>,
661 ) -> Arc<super::TraitEnvironment> {
662 unimplemented!()
663 }
664}
665
666impl<T: ToChalk> ToChalk for super::InEnvironment<T>
667where
668 T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>,
669{
670 type Chalk = chalk_ir::InEnvironment<T::Chalk>;
671
672 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> {
673 chalk_ir::InEnvironment {
674 environment: self.environment.to_chalk(db),
675 goal: self.value.to_chalk(db),
676 }
677 }
678
679 fn from_chalk(
680 db: &dyn HirDatabase,
681 in_env: chalk_ir::InEnvironment<T::Chalk>,
682 ) -> super::InEnvironment<T> {
683 super::InEnvironment {
684 environment: from_chalk(db, in_env.environment),
685 value: from_chalk(db, in_env.goal),
686 }
687 }
688}
689
690impl ToChalk for builtin::BuiltinImplData {
691 type Chalk = ImplDatum;
692
693 fn to_chalk(self, db: &dyn HirDatabase) -> ImplDatum {
694 let impl_type = chalk_rust_ir::ImplType::External;
695 let where_clauses = self.where_clauses.into_iter().map(|w| w.to_chalk(db)).collect();
696
697 let impl_datum_bound =
698 chalk_rust_ir::ImplDatumBound { trait_ref: self.trait_ref.to_chalk(db), where_clauses };
699 let associated_ty_value_ids =
700 self.assoc_ty_values.into_iter().map(|v| v.to_chalk(db)).collect();
701 chalk_rust_ir::ImplDatum {
702 binders: make_binders(impl_datum_bound, self.num_vars),
703 impl_type,
704 polarity: chalk_rust_ir::Polarity::Positive,
705 associated_ty_value_ids,
706 }
707 }
708
709 fn from_chalk(_db: &dyn HirDatabase, _data: ImplDatum) -> Self {
710 unimplemented!()
711 }
712}
713
714impl ToChalk for builtin::BuiltinImplAssocTyValueData {
715 type Chalk = AssociatedTyValue;
716
717 fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValue {
718 let ty = self.value.to_chalk(db);
719 let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty };
720
721 chalk_rust_ir::AssociatedTyValue {
722 associated_ty_id: self.assoc_ty_id.to_chalk(db),
723 impl_id: self.impl_.to_chalk(db),
724 value: make_binders(value_bound, self.num_vars),
725 }
726 }
727
728 fn from_chalk(
729 _db: &dyn HirDatabase,
730 _data: AssociatedTyValue,
731 ) -> builtin::BuiltinImplAssocTyValueData {
732 unimplemented!()
733 }
734}
735
736fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T>
737where
738 T: HasInterner<Interner = Interner>,
739{
740 chalk_ir::Binders::new(
741 chalk_ir::ParameterKinds::from(
742 &Interner,
743 std::iter::repeat(chalk_ir::ParameterKind::Ty(())).take(num_vars),
744 ),
745 value,
746 )
747}
748
749fn convert_where_clauses(
750 db: &dyn HirDatabase,
751 def: GenericDefId,
752 substs: &Substs,
753) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
754 let generic_predicates = db.generic_predicates(def);
755 let mut result = Vec::with_capacity(generic_predicates.len());
756 for pred in generic_predicates.iter() {
757 if pred.value.is_error() {
758 // skip errored predicates completely
759 continue;
760 }
761 result.push(pred.clone().subst(substs).to_chalk(db));
762 }
763 result
764}
765
766fn generic_predicate_to_inline_bound(
767 db: &dyn HirDatabase,
768 pred: &GenericPredicate,
769 self_ty: &Ty,
770) -> Option<chalk_rust_ir::InlineBound<Interner>> {
771 // An InlineBound is like a GenericPredicate, except the self type is left out.
772 // We don't have a special type for this, but Chalk does.
773 match pred {
774 GenericPredicate::Implemented(trait_ref) => {
775 if &trait_ref.substs[0] != self_ty {
776 // we can only convert predicates back to type bounds if they
777 // have the expected self type
778 return None;
779 }
780 let args_no_self = trait_ref.substs[1..]
781 .iter()
782 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
783 .collect();
784 let trait_bound =
785 chalk_rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self };
786 Some(chalk_rust_ir::InlineBound::TraitBound(trait_bound))
787 }
788 GenericPredicate::Projection(proj) => {
789 if &proj.projection_ty.parameters[0] != self_ty {
790 return None;
791 }
792 let trait_ = match proj.projection_ty.associated_ty.lookup(db.upcast()).container {
793 AssocContainerId::TraitId(t) => t,
794 _ => panic!("associated type not in trait"),
795 };
796 let args_no_self = proj.projection_ty.parameters[1..]
797 .iter()
798 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
799 .collect();
800 let alias_eq_bound = chalk_rust_ir::AliasEqBound {
801 value: proj.ty.clone().to_chalk(db),
802 trait_bound: chalk_rust_ir::TraitBound {
803 trait_id: trait_.to_chalk(db),
804 args_no_self,
805 },
806 associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db),
807 parameters: Vec::new(), // FIXME we don't support generic associated types yet
808 };
809 Some(chalk_rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
810 }
811 GenericPredicate::Error => None,
812 }
813}
814
815impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { 41impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
816 fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> { 42 fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> {
817 self.db.associated_ty_data(id) 43 self.db.associated_ty_data(id)
@@ -819,16 +45,24 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
819 fn trait_datum(&self, trait_id: TraitId) -> Arc<TraitDatum> { 45 fn trait_datum(&self, trait_id: TraitId) -> Arc<TraitDatum> {
820 self.db.trait_datum(self.krate, trait_id) 46 self.db.trait_datum(self.krate, trait_id)
821 } 47 }
822 fn struct_datum(&self, struct_id: StructId) -> Arc<StructDatum> { 48 fn adt_datum(&self, struct_id: AdtId) -> Arc<StructDatum> {
823 self.db.struct_datum(self.krate, struct_id) 49 self.db.struct_datum(self.krate, struct_id)
824 } 50 }
825 fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> { 51 fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> {
826 self.db.impl_datum(self.krate, impl_id) 52 self.db.impl_datum(self.krate, impl_id)
827 } 53 }
54
55 fn fn_def_datum(
56 &self,
57 fn_def_id: chalk_ir::FnDefId<Interner>,
58 ) -> Arc<chalk_rust_ir::FnDefDatum<Interner>> {
59 self.db.fn_def_datum(self.krate, fn_def_id)
60 }
61
828 fn impls_for_trait( 62 fn impls_for_trait(
829 &self, 63 &self,
830 trait_id: TraitId, 64 trait_id: TraitId,
831 parameters: &[Parameter<Interner>], 65 parameters: &[GenericArg<Interner>],
832 ) -> Vec<ImplId> { 66 ) -> Vec<ImplId> {
833 debug!("impls_for_trait {:?}", trait_id); 67 debug!("impls_for_trait {:?}", trait_id);
834 let trait_: hir_def::TraitId = from_chalk(self.db, trait_id); 68 let trait_: hir_def::TraitId = from_chalk(self.db, trait_id);
@@ -859,7 +93,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
859 debug!("impls_for_trait returned {} impls", result.len()); 93 debug!("impls_for_trait returned {} impls", result.len());
860 result 94 result
861 } 95 }
862 fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: StructId) -> bool { 96 fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: AdtId) -> bool {
863 debug!("impl_provided_for {:?}, {:?}", auto_trait_id, struct_id); 97 debug!("impl_provided_for {:?}, {:?}", auto_trait_id, struct_id);
864 false // FIXME 98 false // FIXME
865 } 99 }
@@ -878,10 +112,15 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
878 } 112 }
879 fn well_known_trait_id( 113 fn well_known_trait_id(
880 &self, 114 &self,
881 _well_known_trait: chalk_rust_ir::WellKnownTrait, 115 well_known_trait: chalk_rust_ir::WellKnownTrait,
882 ) -> Option<chalk_ir::TraitId<Interner>> { 116 ) -> Option<chalk_ir::TraitId<Interner>> {
883 // FIXME tell Chalk about well-known traits (here and in trait_datum) 117 let lang_attr = lang_attr_from_well_known_trait(well_known_trait);
884 None 118 let lang_items = self.db.crate_lang_items(self.krate);
119 let trait_ = match lang_items.target(lang_attr) {
120 Some(LangItemTarget::TraitId(trait_)) => trait_,
121 _ => return None,
122 };
123 Some(trait_.to_chalk(self.db))
885 } 124 }
886 125
887 fn program_clauses_for_env( 126 fn program_clauses_for_env(
@@ -983,7 +222,8 @@ pub(crate) fn trait_datum_query(
983 let associated_ty_ids = 222 let associated_ty_ids =
984 trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect(); 223 trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect();
985 let trait_datum_bound = chalk_rust_ir::TraitDatumBound { where_clauses }; 224 let trait_datum_bound = chalk_rust_ir::TraitDatumBound { where_clauses };
986 let well_known = None; // FIXME set this (depending on lang items) 225 let well_known =
226 lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name));
987 let trait_datum = TraitDatum { 227 let trait_datum = TraitDatum {
988 id: trait_id, 228 id: trait_id,
989 binders: make_binders(trait_datum_bound, bound_vars.len()), 229 binders: make_binders(trait_datum_bound, bound_vars.len()),
@@ -994,13 +234,32 @@ pub(crate) fn trait_datum_query(
994 Arc::new(trait_datum) 234 Arc::new(trait_datum)
995} 235}
996 236
237fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> {
238 Some(match name {
239 "sized" => WellKnownTrait::SizedTrait,
240 "copy" => WellKnownTrait::CopyTrait,
241 "clone" => WellKnownTrait::CloneTrait,
242 "drop" => WellKnownTrait::DropTrait,
243 _ => return None,
244 })
245}
246
247fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str {
248 match attr {
249 WellKnownTrait::SizedTrait => "sized",
250 WellKnownTrait::CopyTrait => "copy",
251 WellKnownTrait::CloneTrait => "clone",
252 WellKnownTrait::DropTrait => "drop",
253 }
254}
255
997pub(crate) fn struct_datum_query( 256pub(crate) fn struct_datum_query(
998 db: &dyn HirDatabase, 257 db: &dyn HirDatabase,
999 krate: CrateId, 258 krate: CrateId,
1000 struct_id: StructId, 259 struct_id: AdtId,
1001) -> Arc<StructDatum> { 260) -> Arc<StructDatum> {
1002 debug!("struct_datum {:?}", struct_id); 261 debug!("struct_datum {:?}", struct_id);
1003 let type_ctor: TypeCtor = from_chalk(db, TypeName::Struct(struct_id)); 262 let type_ctor: TypeCtor = from_chalk(db, TypeName::Adt(struct_id));
1004 debug!("struct {:?} = {:?}", struct_id, type_ctor); 263 debug!("struct {:?} = {:?}", struct_id, type_ctor);
1005 let num_params = type_ctor.num_ty_params(db); 264 let num_params = type_ctor.num_ty_params(db);
1006 let upstream = type_ctor.krate(db) != Some(krate); 265 let upstream = type_ctor.krate(db) != Some(krate);
@@ -1012,12 +271,12 @@ pub(crate) fn struct_datum_query(
1012 convert_where_clauses(db, generic_def, &bound_vars) 271 convert_where_clauses(db, generic_def, &bound_vars)
1013 }) 272 })
1014 .unwrap_or_else(Vec::new); 273 .unwrap_or_else(Vec::new);
1015 let flags = chalk_rust_ir::StructFlags { 274 let flags = chalk_rust_ir::AdtFlags {
1016 upstream, 275 upstream,
1017 // FIXME set fundamental flag correctly 276 // FIXME set fundamental flag correctly
1018 fundamental: false, 277 fundamental: false,
1019 }; 278 };
1020 let struct_datum_bound = chalk_rust_ir::StructDatumBound { 279 let struct_datum_bound = chalk_rust_ir::AdtDatumBound {
1021 fields: Vec::new(), // FIXME add fields (only relevant for auto traits) 280 fields: Vec::new(), // FIXME add fields (only relevant for auto traits)
1022 where_clauses, 281 where_clauses,
1023 }; 282 };
@@ -1145,15 +404,47 @@ fn type_alias_associated_ty_value(
1145 Arc::new(value) 404 Arc::new(value)
1146} 405}
1147 406
1148impl From<StructId> for crate::TypeCtorId { 407pub(crate) fn fn_def_datum_query(
1149 fn from(struct_id: StructId) -> Self { 408 db: &dyn HirDatabase,
1150 InternKey::from_intern_id(struct_id.0) 409 _krate: CrateId,
410 fn_def_id: FnDefId,
411) -> Arc<FnDefDatum> {
412 let callable_def: CallableDef = from_chalk(db, fn_def_id);
413 let generic_params = generics(db.upcast(), callable_def.into());
414 let sig = db.callable_item_signature(callable_def);
415 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
416 let where_clauses = convert_where_clauses(db, callable_def.into(), &bound_vars);
417 let bound = chalk_rust_ir::FnDefDatumBound {
418 // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway
419 argument_types: sig.value.params().iter().map(|ty| ty.clone().to_chalk(db)).collect(),
420 return_type: sig.value.ret().clone().to_chalk(db),
421 where_clauses,
422 };
423 let datum = FnDefDatum { id: fn_def_id, binders: make_binders(bound, sig.num_binders) };
424 Arc::new(datum)
425}
426
427impl From<AdtId> for crate::TypeCtorId {
428 fn from(struct_id: AdtId) -> Self {
429 struct_id.0
1151 } 430 }
1152} 431}
1153 432
1154impl From<crate::TypeCtorId> for StructId { 433impl From<crate::TypeCtorId> for AdtId {
1155 fn from(type_ctor_id: crate::TypeCtorId) -> Self { 434 fn from(type_ctor_id: crate::TypeCtorId) -> Self {
1156 chalk_ir::StructId(type_ctor_id.as_intern_id()) 435 chalk_ir::AdtId(type_ctor_id)
436 }
437}
438
439impl From<FnDefId> for crate::CallableDefId {
440 fn from(fn_def_id: FnDefId) -> Self {
441 InternKey::from_intern_id(fn_def_id.0)
442 }
443}
444
445impl From<crate::CallableDefId> for FnDefId {
446 fn from(callable_def_id: crate::CallableDefId) -> Self {
447 chalk_ir::FnDefId(callable_def_id.as_intern_id())
1157 } 448 }
1158} 449}
1159 450
diff --git a/crates/ra_hir_ty/src/traits/chalk/interner.rs b/crates/ra_hir_ty/src/traits/chalk/interner.rs
new file mode 100644
index 000000000..2a27f8ed8
--- /dev/null
+++ b/crates/ra_hir_ty/src/traits/chalk/interner.rs
@@ -0,0 +1,353 @@
1//! Implementation of the Chalk `Interner` trait, which allows customizing the
2//! representation of the various objects Chalk deals with (types, goals etc.).
3
4use super::tls;
5use chalk_ir::{GenericArg, Goal, GoalData};
6use hir_def::TypeAliasId;
7use ra_db::salsa::InternId;
8use std::{fmt, sync::Arc};
9
10#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
11pub struct Interner;
12
13pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
14pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum<Interner>;
15pub type TraitId = chalk_ir::TraitId<Interner>;
16pub type TraitDatum = chalk_rust_ir::TraitDatum<Interner>;
17pub type AdtId = chalk_ir::AdtId<Interner>;
18pub type StructDatum = chalk_rust_ir::AdtDatum<Interner>;
19pub type ImplId = chalk_ir::ImplId<Interner>;
20pub type ImplDatum = chalk_rust_ir::ImplDatum<Interner>;
21pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId<Interner>;
22pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue<Interner>;
23pub type FnDefId = chalk_ir::FnDefId<Interner>;
24pub type FnDefDatum = chalk_rust_ir::FnDefDatum<Interner>;
25
26impl chalk_ir::interner::Interner for Interner {
27 type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc?
28 type InternedLifetime = chalk_ir::LifetimeData<Self>;
29 type InternedConst = Arc<chalk_ir::ConstData<Self>>;
30 type InternedConcreteConst = ();
31 type InternedGenericArg = chalk_ir::GenericArgData<Self>;
32 type InternedGoal = Arc<GoalData<Self>>;
33 type InternedGoals = Vec<Goal<Self>>;
34 type InternedSubstitution = Vec<GenericArg<Self>>;
35 type InternedProgramClause = chalk_ir::ProgramClauseData<Self>;
36 type InternedProgramClauses = Arc<[chalk_ir::ProgramClause<Self>]>;
37 type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>;
38 type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>;
39 type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>;
40 type DefId = InternId;
41 type InternedAdtId = crate::TypeCtorId;
42 type Identifier = TypeAliasId;
43
44 fn debug_adt_id(type_kind_id: AdtId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
45 tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt)))
46 }
47
48 fn debug_trait_id(type_kind_id: TraitId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
49 tls::with_current_program(|prog| Some(prog?.debug_trait_id(type_kind_id, fmt)))
50 }
51
52 fn debug_assoc_type_id(id: AssocTypeId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
53 tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt)))
54 }
55
56 fn debug_alias(
57 alias: &chalk_ir::AliasTy<Interner>,
58 fmt: &mut fmt::Formatter<'_>,
59 ) -> Option<fmt::Result> {
60 tls::with_current_program(|prog| Some(prog?.debug_alias(alias, fmt)))
61 }
62
63 fn debug_projection_ty(
64 proj: &chalk_ir::ProjectionTy<Interner>,
65 fmt: &mut fmt::Formatter<'_>,
66 ) -> Option<fmt::Result> {
67 tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt)))
68 }
69
70 fn debug_opaque_ty(
71 opaque_ty: &chalk_ir::OpaqueTy<Interner>,
72 fmt: &mut fmt::Formatter<'_>,
73 ) -> Option<fmt::Result> {
74 tls::with_current_program(|prog| Some(prog?.debug_opaque_ty(opaque_ty, fmt)))
75 }
76
77 fn debug_opaque_ty_id(
78 opaque_ty_id: chalk_ir::OpaqueTyId<Self>,
79 fmt: &mut fmt::Formatter<'_>,
80 ) -> Option<fmt::Result> {
81 tls::with_current_program(|prog| Some(prog?.debug_opaque_ty_id(opaque_ty_id, fmt)))
82 }
83
84 fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
85 tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt)))
86 }
87
88 fn debug_lifetime(
89 lifetime: &chalk_ir::Lifetime<Interner>,
90 fmt: &mut fmt::Formatter<'_>,
91 ) -> Option<fmt::Result> {
92 tls::with_current_program(|prog| Some(prog?.debug_lifetime(lifetime, fmt)))
93 }
94
95 fn debug_generic_arg(
96 parameter: &GenericArg<Interner>,
97 fmt: &mut fmt::Formatter<'_>,
98 ) -> Option<fmt::Result> {
99 tls::with_current_program(|prog| Some(prog?.debug_generic_arg(parameter, fmt)))
100 }
101
102 fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
103 tls::with_current_program(|prog| Some(prog?.debug_goal(goal, fmt)))
104 }
105
106 fn debug_goals(
107 goals: &chalk_ir::Goals<Interner>,
108 fmt: &mut fmt::Formatter<'_>,
109 ) -> Option<fmt::Result> {
110 tls::with_current_program(|prog| Some(prog?.debug_goals(goals, fmt)))
111 }
112
113 fn debug_program_clause_implication(
114 pci: &chalk_ir::ProgramClauseImplication<Interner>,
115 fmt: &mut fmt::Formatter<'_>,
116 ) -> Option<fmt::Result> {
117 tls::with_current_program(|prog| Some(prog?.debug_program_clause_implication(pci, fmt)))
118 }
119
120 fn debug_application_ty(
121 application_ty: &chalk_ir::ApplicationTy<Interner>,
122 fmt: &mut fmt::Formatter<'_>,
123 ) -> Option<fmt::Result> {
124 tls::with_current_program(|prog| Some(prog?.debug_application_ty(application_ty, fmt)))
125 }
126
127 fn debug_substitution(
128 substitution: &chalk_ir::Substitution<Interner>,
129 fmt: &mut fmt::Formatter<'_>,
130 ) -> Option<fmt::Result> {
131 tls::with_current_program(|prog| Some(prog?.debug_substitution(substitution, fmt)))
132 }
133
134 fn debug_separator_trait_ref(
135 separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>,
136 fmt: &mut fmt::Formatter<'_>,
137 ) -> Option<fmt::Result> {
138 tls::with_current_program(|prog| {
139 Some(prog?.debug_separator_trait_ref(separator_trait_ref, fmt))
140 })
141 }
142
143 fn debug_fn_def_id(
144 fn_def_id: chalk_ir::FnDefId<Self>,
145 fmt: &mut fmt::Formatter<'_>,
146 ) -> Option<fmt::Result> {
147 tls::with_current_program(|prog| Some(prog?.debug_fn_def_id(fn_def_id, fmt)))
148 }
149 fn debug_const(
150 constant: &chalk_ir::Const<Self>,
151 fmt: &mut fmt::Formatter<'_>,
152 ) -> Option<fmt::Result> {
153 tls::with_current_program(|prog| Some(prog?.debug_const(constant, fmt)))
154 }
155 fn debug_variable_kinds(
156 variable_kinds: &chalk_ir::VariableKinds<Self>,
157 fmt: &mut fmt::Formatter<'_>,
158 ) -> Option<fmt::Result> {
159 tls::with_current_program(|prog| Some(prog?.debug_variable_kinds(variable_kinds, fmt)))
160 }
161 fn debug_variable_kinds_with_angles(
162 variable_kinds: &chalk_ir::VariableKinds<Self>,
163 fmt: &mut fmt::Formatter<'_>,
164 ) -> Option<fmt::Result> {
165 tls::with_current_program(|prog| {
166 Some(prog?.debug_variable_kinds_with_angles(variable_kinds, fmt))
167 })
168 }
169 fn debug_canonical_var_kinds(
170 canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Self>,
171 fmt: &mut fmt::Formatter<'_>,
172 ) -> Option<fmt::Result> {
173 tls::with_current_program(|prog| {
174 Some(prog?.debug_canonical_var_kinds(canonical_var_kinds, fmt))
175 })
176 }
177 fn debug_program_clause(
178 clause: &chalk_ir::ProgramClause<Self>,
179 fmt: &mut fmt::Formatter<'_>,
180 ) -> Option<fmt::Result> {
181 tls::with_current_program(|prog| Some(prog?.debug_program_clause(clause, fmt)))
182 }
183 fn debug_program_clauses(
184 clauses: &chalk_ir::ProgramClauses<Self>,
185 fmt: &mut fmt::Formatter<'_>,
186 ) -> Option<fmt::Result> {
187 tls::with_current_program(|prog| Some(prog?.debug_program_clauses(clauses, fmt)))
188 }
189 fn debug_quantified_where_clauses(
190 clauses: &chalk_ir::QuantifiedWhereClauses<Self>,
191 fmt: &mut fmt::Formatter<'_>,
192 ) -> Option<fmt::Result> {
193 tls::with_current_program(|prog| Some(prog?.debug_quantified_where_clauses(clauses, fmt)))
194 }
195
196 fn intern_ty(&self, ty: chalk_ir::TyData<Self>) -> Box<chalk_ir::TyData<Self>> {
197 Box::new(ty)
198 }
199
200 fn ty_data<'a>(&self, ty: &'a Box<chalk_ir::TyData<Self>>) -> &'a chalk_ir::TyData<Self> {
201 ty
202 }
203
204 fn intern_lifetime(
205 &self,
206 lifetime: chalk_ir::LifetimeData<Self>,
207 ) -> chalk_ir::LifetimeData<Self> {
208 lifetime
209 }
210
211 fn lifetime_data<'a>(
212 &self,
213 lifetime: &'a chalk_ir::LifetimeData<Self>,
214 ) -> &'a chalk_ir::LifetimeData<Self> {
215 lifetime
216 }
217
218 fn intern_const(&self, constant: chalk_ir::ConstData<Self>) -> Arc<chalk_ir::ConstData<Self>> {
219 Arc::new(constant)
220 }
221
222 fn const_data<'a>(
223 &self,
224 constant: &'a Arc<chalk_ir::ConstData<Self>>,
225 ) -> &'a chalk_ir::ConstData<Self> {
226 constant
227 }
228
229 fn const_eq(&self, _ty: &Box<chalk_ir::TyData<Self>>, _c1: &(), _c2: &()) -> bool {
230 true
231 }
232
233 fn intern_generic_arg(
234 &self,
235 parameter: chalk_ir::GenericArgData<Self>,
236 ) -> chalk_ir::GenericArgData<Self> {
237 parameter
238 }
239
240 fn generic_arg_data<'a>(
241 &self,
242 parameter: &'a chalk_ir::GenericArgData<Self>,
243 ) -> &'a chalk_ir::GenericArgData<Self> {
244 parameter
245 }
246
247 fn intern_goal(&self, goal: GoalData<Self>) -> Arc<GoalData<Self>> {
248 Arc::new(goal)
249 }
250
251 fn intern_goals<E>(
252 &self,
253 data: impl IntoIterator<Item = Result<Goal<Self>, E>>,
254 ) -> Result<Self::InternedGoals, E> {
255 data.into_iter().collect()
256 }
257
258 fn goal_data<'a>(&self, goal: &'a Arc<GoalData<Self>>) -> &'a GoalData<Self> {
259 goal
260 }
261
262 fn goals_data<'a>(&self, goals: &'a Vec<Goal<Interner>>) -> &'a [Goal<Interner>] {
263 goals
264 }
265
266 fn intern_substitution<E>(
267 &self,
268 data: impl IntoIterator<Item = Result<GenericArg<Self>, E>>,
269 ) -> Result<Vec<GenericArg<Self>>, E> {
270 data.into_iter().collect()
271 }
272
273 fn substitution_data<'a>(
274 &self,
275 substitution: &'a Vec<GenericArg<Self>>,
276 ) -> &'a [GenericArg<Self>] {
277 substitution
278 }
279
280 fn intern_program_clause(
281 &self,
282 data: chalk_ir::ProgramClauseData<Self>,
283 ) -> chalk_ir::ProgramClauseData<Self> {
284 data
285 }
286
287 fn program_clause_data<'a>(
288 &self,
289 clause: &'a chalk_ir::ProgramClauseData<Self>,
290 ) -> &'a chalk_ir::ProgramClauseData<Self> {
291 clause
292 }
293
294 fn intern_program_clauses<E>(
295 &self,
296 data: impl IntoIterator<Item = Result<chalk_ir::ProgramClause<Self>, E>>,
297 ) -> Result<Arc<[chalk_ir::ProgramClause<Self>]>, E> {
298 data.into_iter().collect()
299 }
300
301 fn program_clauses_data<'a>(
302 &self,
303 clauses: &'a Arc<[chalk_ir::ProgramClause<Self>]>,
304 ) -> &'a [chalk_ir::ProgramClause<Self>] {
305 &clauses
306 }
307
308 fn intern_quantified_where_clauses<E>(
309 &self,
310 data: impl IntoIterator<Item = Result<chalk_ir::QuantifiedWhereClause<Self>, E>>,
311 ) -> Result<Self::InternedQuantifiedWhereClauses, E> {
312 data.into_iter().collect()
313 }
314
315 fn quantified_where_clauses_data<'a>(
316 &self,
317 clauses: &'a Self::InternedQuantifiedWhereClauses,
318 ) -> &'a [chalk_ir::QuantifiedWhereClause<Self>] {
319 clauses
320 }
321
322 fn intern_generic_arg_kinds<E>(
323 &self,
324 data: impl IntoIterator<Item = Result<chalk_ir::VariableKind<Self>, E>>,
325 ) -> Result<Self::InternedVariableKinds, E> {
326 data.into_iter().collect()
327 }
328
329 fn variable_kinds_data<'a>(
330 &self,
331 parameter_kinds: &'a Self::InternedVariableKinds,
332 ) -> &'a [chalk_ir::VariableKind<Self>] {
333 &parameter_kinds
334 }
335
336 fn intern_canonical_var_kinds<E>(
337 &self,
338 data: impl IntoIterator<Item = Result<chalk_ir::CanonicalVarKind<Self>, E>>,
339 ) -> Result<Self::InternedCanonicalVarKinds, E> {
340 data.into_iter().collect()
341 }
342
343 fn canonical_var_kinds_data<'a>(
344 &self,
345 canonical_var_kinds: &'a Self::InternedCanonicalVarKinds,
346 ) -> &'a [chalk_ir::CanonicalVarKind<Self>] {
347 &canonical_var_kinds
348 }
349}
350
351impl chalk_ir::interner::HasInterner for Interner {
352 type Interner = Self;
353}
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
new file mode 100644
index 000000000..7082cb095
--- /dev/null
+++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
@@ -0,0 +1,701 @@
1//! This module contains the implementations of the `ToChalk` trait, which
2//! handles conversion between our data types and their corresponding types in
3//! Chalk (in both directions); plus some helper functions for more specialized
4//! conversions.
5
6use chalk_ir::{
7 cast::Cast, fold::shift::Shift, interner::HasInterner, PlaceholderIndex, Scalar, TypeName,
8 UniverseIndex,
9};
10
11use hir_def::{type_ref::Mutability, AssocContainerId, GenericDefId, Lookup, TypeAliasId};
12use ra_db::salsa::InternKey;
13
14use crate::{
15 db::HirDatabase,
16 primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain},
17 traits::{builtin, AssocTyValue, Canonical, Impl, Obligation},
18 ApplicationTy, CallableDef, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy,
19 Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
20};
21
22use super::interner::*;
23use super::*;
24
25impl ToChalk for Ty {
26 type Chalk = chalk_ir::Ty<Interner>;
27 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty<Interner> {
28 match self {
29 Ty::Apply(apply_ty) => match apply_ty.ctor {
30 TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters),
31 TypeCtor::FnPtr { num_args: _ } => {
32 let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner);
33 chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: 0, substitution })
34 .intern(&Interner)
35 }
36 _ => {
37 let name = apply_ty.ctor.to_chalk(db);
38 let substitution = apply_ty.parameters.to_chalk(db);
39 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
40 }
41 },
42 Ty::Projection(proj_ty) => {
43 let associated_ty_id = proj_ty.associated_ty.to_chalk(db);
44 let substitution = proj_ty.parameters.to_chalk(db);
45 chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
46 associated_ty_id,
47 substitution,
48 })
49 .cast(&Interner)
50 .intern(&Interner)
51 }
52 Ty::Placeholder(id) => {
53 let interned_id = db.intern_type_param_id(id);
54 PlaceholderIndex {
55 ui: UniverseIndex::ROOT,
56 idx: interned_id.as_intern_id().as_usize(),
57 }
58 .to_ty::<Interner>(&Interner)
59 }
60 Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx).intern(&Interner),
61 Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"),
62 Ty::Dyn(predicates) => {
63 let where_clauses = chalk_ir::QuantifiedWhereClauses::from(
64 &Interner,
65 predicates.iter().filter(|p| !p.is_error()).cloned().map(|p| p.to_chalk(db)),
66 );
67 let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) };
68 chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner)
69 }
70 Ty::Opaque(_) | Ty::Unknown => {
71 let substitution = chalk_ir::Substitution::empty(&Interner);
72 let name = TypeName::Error;
73 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
74 }
75 }
76 }
77 fn from_chalk(db: &dyn HirDatabase, chalk: chalk_ir::Ty<Interner>) -> Self {
78 match chalk.data(&Interner).clone() {
79 chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name {
80 TypeName::Error => Ty::Unknown,
81 TypeName::Ref(m) => ref_from_chalk(db, m, apply_ty.substitution),
82 _ => {
83 let ctor = from_chalk(db, apply_ty.name);
84 let parameters = from_chalk(db, apply_ty.substitution);
85 Ty::Apply(ApplicationTy { ctor, parameters })
86 }
87 },
88 chalk_ir::TyData::Placeholder(idx) => {
89 assert_eq!(idx.ui, UniverseIndex::ROOT);
90 let interned_id = crate::db::GlobalTypeParamId::from_intern_id(
91 crate::salsa::InternId::from(idx.idx),
92 );
93 Ty::Placeholder(db.lookup_intern_type_param_id(interned_id))
94 }
95 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => {
96 let associated_ty = from_chalk(db, proj.associated_ty_id);
97 let parameters = from_chalk(db, proj.substitution);
98 Ty::Projection(ProjectionTy { associated_ty, parameters })
99 }
100 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(),
101 chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => {
102 let parameters: Substs = from_chalk(db, substitution);
103 Ty::Apply(ApplicationTy {
104 ctor: TypeCtor::FnPtr { num_args: (parameters.len() - 1) as u16 },
105 parameters,
106 })
107 }
108 chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx),
109 chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown,
110 chalk_ir::TyData::Dyn(where_clauses) => {
111 assert_eq!(where_clauses.bounds.binders.len(&Interner), 1);
112 let predicates = where_clauses
113 .bounds
114 .skip_binders()
115 .iter(&Interner)
116 .map(|c| from_chalk(db, c.clone()))
117 .collect();
118 Ty::Dyn(predicates)
119 }
120 }
121 }
122}
123
124const LIFETIME_PLACEHOLDER: PlaceholderIndex =
125 PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::MAX };
126
127/// We currently don't model lifetimes, but Chalk does. So, we have to insert a
128/// fake lifetime here, because Chalks built-in logic may expect it to be there.
129fn ref_to_chalk(
130 db: &dyn HirDatabase,
131 mutability: Mutability,
132 subst: Substs,
133) -> chalk_ir::Ty<Interner> {
134 let arg = subst[0].clone().to_chalk(db);
135 let lifetime = LIFETIME_PLACEHOLDER.to_lifetime(&Interner);
136 chalk_ir::ApplicationTy {
137 name: TypeName::Ref(mutability.to_chalk(db)),
138 substitution: chalk_ir::Substitution::from(
139 &Interner,
140 vec![lifetime.cast(&Interner), arg.cast(&Interner)],
141 ),
142 }
143 .intern(&Interner)
144}
145
146/// Here we remove the lifetime from the type we got from Chalk.
147fn ref_from_chalk(
148 db: &dyn HirDatabase,
149 mutability: chalk_ir::Mutability,
150 subst: chalk_ir::Substitution<Interner>,
151) -> Ty {
152 let tys = subst
153 .iter(&Interner)
154 .filter_map(|p| Some(from_chalk(db, p.ty(&Interner)?.clone())))
155 .collect();
156 Ty::apply(TypeCtor::Ref(from_chalk(db, mutability)), Substs(tys))
157}
158
159impl ToChalk for Substs {
160 type Chalk = chalk_ir::Substitution<Interner>;
161
162 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Substitution<Interner> {
163 chalk_ir::Substitution::from(&Interner, self.iter().map(|ty| ty.clone().to_chalk(db)))
164 }
165
166 fn from_chalk(db: &dyn HirDatabase, parameters: chalk_ir::Substitution<Interner>) -> Substs {
167 let tys = parameters
168 .iter(&Interner)
169 .map(|p| match p.ty(&Interner) {
170 Some(ty) => from_chalk(db, ty.clone()),
171 None => unimplemented!(),
172 })
173 .collect();
174 Substs(tys)
175 }
176}
177
178impl ToChalk for TraitRef {
179 type Chalk = chalk_ir::TraitRef<Interner>;
180
181 fn to_chalk(self: TraitRef, db: &dyn HirDatabase) -> chalk_ir::TraitRef<Interner> {
182 let trait_id = self.trait_.to_chalk(db);
183 let substitution = self.substs.to_chalk(db);
184 chalk_ir::TraitRef { trait_id, substitution }
185 }
186
187 fn from_chalk(db: &dyn HirDatabase, trait_ref: chalk_ir::TraitRef<Interner>) -> Self {
188 let trait_ = from_chalk(db, trait_ref.trait_id);
189 let substs = from_chalk(db, trait_ref.substitution);
190 TraitRef { trait_, substs }
191 }
192}
193
194impl ToChalk for hir_def::TraitId {
195 type Chalk = TraitId;
196
197 fn to_chalk(self, _db: &dyn HirDatabase) -> TraitId {
198 chalk_ir::TraitId(self.as_intern_id())
199 }
200
201 fn from_chalk(_db: &dyn HirDatabase, trait_id: TraitId) -> hir_def::TraitId {
202 InternKey::from_intern_id(trait_id.0)
203 }
204}
205
206impl ToChalk for TypeCtor {
207 type Chalk = TypeName<Interner>;
208
209 fn to_chalk(self, db: &dyn HirDatabase) -> TypeName<Interner> {
210 match self {
211 TypeCtor::AssociatedType(type_alias) => {
212 let type_id = type_alias.to_chalk(db);
213 TypeName::AssociatedType(type_id)
214 }
215
216 TypeCtor::Bool => TypeName::Scalar(Scalar::Bool),
217 TypeCtor::Char => TypeName::Scalar(Scalar::Char),
218 TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)),
219 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 })) => {
220 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32))
221 }
222 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 })) => {
223 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64))
224 }
225
226 TypeCtor::Tuple { cardinality } => TypeName::Tuple(cardinality.into()),
227 TypeCtor::RawPtr(mutability) => TypeName::Raw(mutability.to_chalk(db)),
228 TypeCtor::Slice => TypeName::Slice,
229 TypeCtor::Ref(mutability) => TypeName::Ref(mutability.to_chalk(db)),
230 TypeCtor::Str => TypeName::Str,
231 TypeCtor::FnDef(callable_def) => {
232 let id = callable_def.to_chalk(db);
233 TypeName::FnDef(id)
234 }
235 TypeCtor::Int(Uncertain::Unknown)
236 | TypeCtor::Float(Uncertain::Unknown)
237 | TypeCtor::Adt(_)
238 | TypeCtor::Array
239 | TypeCtor::FnPtr { .. }
240 | TypeCtor::Never
241 | TypeCtor::Closure { .. } => {
242 // other TypeCtors get interned and turned into a chalk StructId
243 let struct_id = db.intern_type_ctor(self).into();
244 TypeName::Adt(struct_id)
245 }
246 }
247 }
248
249 fn from_chalk(db: &dyn HirDatabase, type_name: TypeName<Interner>) -> TypeCtor {
250 match type_name {
251 TypeName::Adt(struct_id) => db.lookup_intern_type_ctor(struct_id.into()),
252 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
253 TypeName::OpaqueType(_) => unreachable!(),
254
255 TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool,
256 TypeName::Scalar(Scalar::Char) => TypeCtor::Char,
257 TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(Uncertain::Known(IntTy {
258 signedness: Signedness::Signed,
259 bitness: bitness_from_chalk_int(int_ty),
260 })),
261 TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(Uncertain::Known(IntTy {
262 signedness: Signedness::Unsigned,
263 bitness: bitness_from_chalk_uint(uint_ty),
264 })),
265 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) => {
266 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 }))
267 }
268 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) => {
269 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 }))
270 }
271 TypeName::Tuple(cardinality) => TypeCtor::Tuple { cardinality: cardinality as u16 },
272 TypeName::Raw(mutability) => TypeCtor::RawPtr(from_chalk(db, mutability)),
273 TypeName::Slice => TypeCtor::Slice,
274 TypeName::Ref(mutability) => TypeCtor::Ref(from_chalk(db, mutability)),
275 TypeName::Str => TypeCtor::Str,
276
277 TypeName::FnDef(fn_def_id) => {
278 let callable_def = from_chalk(db, fn_def_id);
279 TypeCtor::FnDef(callable_def)
280 }
281
282 TypeName::Error => {
283 // this should not be reached, since we don't represent TypeName::Error with TypeCtor
284 unreachable!()
285 }
286 }
287 }
288}
289
290fn bitness_from_chalk_uint(uint_ty: chalk_ir::UintTy) -> IntBitness {
291 use chalk_ir::UintTy;
292
293 match uint_ty {
294 UintTy::Usize => IntBitness::Xsize,
295 UintTy::U8 => IntBitness::X8,
296 UintTy::U16 => IntBitness::X16,
297 UintTy::U32 => IntBitness::X32,
298 UintTy::U64 => IntBitness::X64,
299 UintTy::U128 => IntBitness::X128,
300 }
301}
302
303fn bitness_from_chalk_int(int_ty: chalk_ir::IntTy) -> IntBitness {
304 use chalk_ir::IntTy;
305
306 match int_ty {
307 IntTy::Isize => IntBitness::Xsize,
308 IntTy::I8 => IntBitness::X8,
309 IntTy::I16 => IntBitness::X16,
310 IntTy::I32 => IntBitness::X32,
311 IntTy::I64 => IntBitness::X64,
312 IntTy::I128 => IntBitness::X128,
313 }
314}
315
316fn int_ty_to_chalk(int_ty: IntTy) -> Scalar {
317 use chalk_ir::{IntTy, UintTy};
318
319 match int_ty.signedness {
320 Signedness::Signed => Scalar::Int(match int_ty.bitness {
321 IntBitness::Xsize => IntTy::Isize,
322 IntBitness::X8 => IntTy::I8,
323 IntBitness::X16 => IntTy::I16,
324 IntBitness::X32 => IntTy::I32,
325 IntBitness::X64 => IntTy::I64,
326 IntBitness::X128 => IntTy::I128,
327 }),
328 Signedness::Unsigned => Scalar::Uint(match int_ty.bitness {
329 IntBitness::Xsize => UintTy::Usize,
330 IntBitness::X8 => UintTy::U8,
331 IntBitness::X16 => UintTy::U16,
332 IntBitness::X32 => UintTy::U32,
333 IntBitness::X64 => UintTy::U64,
334 IntBitness::X128 => UintTy::U128,
335 }),
336 }
337}
338
339impl ToChalk for Mutability {
340 type Chalk = chalk_ir::Mutability;
341 fn to_chalk(self, _db: &dyn HirDatabase) -> Self::Chalk {
342 match self {
343 Mutability::Shared => chalk_ir::Mutability::Not,
344 Mutability::Mut => chalk_ir::Mutability::Mut,
345 }
346 }
347 fn from_chalk(_db: &dyn HirDatabase, chalk: Self::Chalk) -> Self {
348 match chalk {
349 chalk_ir::Mutability::Mut => Mutability::Mut,
350 chalk_ir::Mutability::Not => Mutability::Shared,
351 }
352 }
353}
354
355impl ToChalk for Impl {
356 type Chalk = ImplId;
357
358 fn to_chalk(self, db: &dyn HirDatabase) -> ImplId {
359 db.intern_chalk_impl(self).into()
360 }
361
362 fn from_chalk(db: &dyn HirDatabase, impl_id: ImplId) -> Impl {
363 db.lookup_intern_chalk_impl(impl_id.into())
364 }
365}
366
367impl ToChalk for CallableDef {
368 type Chalk = FnDefId;
369
370 fn to_chalk(self, db: &dyn HirDatabase) -> FnDefId {
371 db.intern_callable_def(self).into()
372 }
373
374 fn from_chalk(db: &dyn HirDatabase, fn_def_id: FnDefId) -> CallableDef {
375 db.lookup_intern_callable_def(fn_def_id.into())
376 }
377}
378
379impl ToChalk for TypeAliasId {
380 type Chalk = AssocTypeId;
381
382 fn to_chalk(self, _db: &dyn HirDatabase) -> AssocTypeId {
383 chalk_ir::AssocTypeId(self.as_intern_id())
384 }
385
386 fn from_chalk(_db: &dyn HirDatabase, type_alias_id: AssocTypeId) -> TypeAliasId {
387 InternKey::from_intern_id(type_alias_id.0)
388 }
389}
390
391impl ToChalk for AssocTyValue {
392 type Chalk = AssociatedTyValueId;
393
394 fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValueId {
395 db.intern_assoc_ty_value(self).into()
396 }
397
398 fn from_chalk(db: &dyn HirDatabase, assoc_ty_value_id: AssociatedTyValueId) -> AssocTyValue {
399 db.lookup_intern_assoc_ty_value(assoc_ty_value_id.into())
400 }
401}
402
403impl ToChalk for GenericPredicate {
404 type Chalk = chalk_ir::QuantifiedWhereClause<Interner>;
405
406 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::QuantifiedWhereClause<Interner> {
407 match self {
408 GenericPredicate::Implemented(trait_ref) => {
409 let chalk_trait_ref = trait_ref.to_chalk(db);
410 let chalk_trait_ref = chalk_trait_ref.shifted_in(&Interner);
411 make_binders(chalk_ir::WhereClause::Implemented(chalk_trait_ref), 0)
412 }
413 GenericPredicate::Projection(projection_pred) => {
414 let ty = projection_pred.ty.to_chalk(db).shifted_in(&Interner);
415 let projection = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner);
416 let alias = chalk_ir::AliasTy::Projection(projection);
417 make_binders(chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), 0)
418 }
419 GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"),
420 }
421 }
422
423 fn from_chalk(
424 db: &dyn HirDatabase,
425 where_clause: chalk_ir::QuantifiedWhereClause<Interner>,
426 ) -> GenericPredicate {
427 // we don't produce any where clauses with binders and can't currently deal with them
428 match where_clause
429 .skip_binders()
430 .shifted_out(&Interner)
431 .expect("unexpected bound vars in where clause")
432 {
433 chalk_ir::WhereClause::Implemented(tr) => {
434 GenericPredicate::Implemented(from_chalk(db, tr))
435 }
436 chalk_ir::WhereClause::AliasEq(projection_eq) => {
437 let projection_ty = from_chalk(
438 db,
439 match projection_eq.alias {
440 chalk_ir::AliasTy::Projection(p) => p,
441 _ => unimplemented!(),
442 },
443 );
444 let ty = from_chalk(db, projection_eq.ty);
445 GenericPredicate::Projection(ProjectionPredicate { projection_ty, ty })
446 }
447 }
448 }
449}
450
451impl ToChalk for ProjectionTy {
452 type Chalk = chalk_ir::ProjectionTy<Interner>;
453
454 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy<Interner> {
455 chalk_ir::ProjectionTy {
456 associated_ty_id: self.associated_ty.to_chalk(db),
457 substitution: self.parameters.to_chalk(db),
458 }
459 }
460
461 fn from_chalk(
462 db: &dyn HirDatabase,
463 projection_ty: chalk_ir::ProjectionTy<Interner>,
464 ) -> ProjectionTy {
465 ProjectionTy {
466 associated_ty: from_chalk(db, projection_ty.associated_ty_id),
467 parameters: from_chalk(db, projection_ty.substitution),
468 }
469 }
470}
471
472impl ToChalk for ProjectionPredicate {
473 type Chalk = chalk_ir::AliasEq<Interner>;
474
475 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> {
476 chalk_ir::AliasEq {
477 alias: chalk_ir::AliasTy::Projection(self.projection_ty.to_chalk(db)),
478 ty: self.ty.to_chalk(db),
479 }
480 }
481
482 fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq<Interner>) -> Self {
483 unimplemented!()
484 }
485}
486
487impl ToChalk for Obligation {
488 type Chalk = chalk_ir::DomainGoal<Interner>;
489
490 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::DomainGoal<Interner> {
491 match self {
492 Obligation::Trait(tr) => tr.to_chalk(db).cast(&Interner),
493 Obligation::Projection(pr) => pr.to_chalk(db).cast(&Interner),
494 }
495 }
496
497 fn from_chalk(_db: &dyn HirDatabase, _goal: chalk_ir::DomainGoal<Interner>) -> Self {
498 unimplemented!()
499 }
500}
501
502impl<T> ToChalk for Canonical<T>
503where
504 T: ToChalk,
505 T::Chalk: HasInterner<Interner = Interner>,
506{
507 type Chalk = chalk_ir::Canonical<T::Chalk>;
508
509 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> {
510 let parameter = chalk_ir::CanonicalVarKind::new(
511 chalk_ir::VariableKind::Ty,
512 chalk_ir::UniverseIndex::ROOT,
513 );
514 let value = self.value.to_chalk(db);
515 chalk_ir::Canonical {
516 value,
517 binders: chalk_ir::CanonicalVarKinds::from(&Interner, vec![parameter; self.num_vars]),
518 }
519 }
520
521 fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> {
522 Canonical {
523 num_vars: canonical.binders.len(&Interner),
524 value: from_chalk(db, canonical.value),
525 }
526 }
527}
528
529impl ToChalk for Arc<TraitEnvironment> {
530 type Chalk = chalk_ir::Environment<Interner>;
531
532 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Environment<Interner> {
533 let mut clauses = Vec::new();
534 for pred in &self.predicates {
535 if pred.is_error() {
536 // for env, we just ignore errors
537 continue;
538 }
539 let program_clause: chalk_ir::ProgramClause<Interner> =
540 pred.clone().to_chalk(db).cast(&Interner);
541 clauses.push(program_clause.into_from_env_clause(&Interner));
542 }
543 chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses)
544 }
545
546 fn from_chalk(
547 _db: &dyn HirDatabase,
548 _env: chalk_ir::Environment<Interner>,
549 ) -> Arc<TraitEnvironment> {
550 unimplemented!()
551 }
552}
553
554impl<T: ToChalk> ToChalk for InEnvironment<T>
555where
556 T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>,
557{
558 type Chalk = chalk_ir::InEnvironment<T::Chalk>;
559
560 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> {
561 chalk_ir::InEnvironment {
562 environment: self.environment.to_chalk(db),
563 goal: self.value.to_chalk(db),
564 }
565 }
566
567 fn from_chalk(
568 db: &dyn HirDatabase,
569 in_env: chalk_ir::InEnvironment<T::Chalk>,
570 ) -> InEnvironment<T> {
571 InEnvironment {
572 environment: from_chalk(db, in_env.environment),
573 value: from_chalk(db, in_env.goal),
574 }
575 }
576}
577
578impl ToChalk for builtin::BuiltinImplData {
579 type Chalk = ImplDatum;
580
581 fn to_chalk(self, db: &dyn HirDatabase) -> ImplDatum {
582 let impl_type = chalk_rust_ir::ImplType::External;
583 let where_clauses = self.where_clauses.into_iter().map(|w| w.to_chalk(db)).collect();
584
585 let impl_datum_bound =
586 chalk_rust_ir::ImplDatumBound { trait_ref: self.trait_ref.to_chalk(db), where_clauses };
587 let associated_ty_value_ids =
588 self.assoc_ty_values.into_iter().map(|v| v.to_chalk(db)).collect();
589 chalk_rust_ir::ImplDatum {
590 binders: make_binders(impl_datum_bound, self.num_vars),
591 impl_type,
592 polarity: chalk_rust_ir::Polarity::Positive,
593 associated_ty_value_ids,
594 }
595 }
596
597 fn from_chalk(_db: &dyn HirDatabase, _data: ImplDatum) -> Self {
598 unimplemented!()
599 }
600}
601
602impl ToChalk for builtin::BuiltinImplAssocTyValueData {
603 type Chalk = AssociatedTyValue;
604
605 fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValue {
606 let ty = self.value.to_chalk(db);
607 let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty };
608
609 chalk_rust_ir::AssociatedTyValue {
610 associated_ty_id: self.assoc_ty_id.to_chalk(db),
611 impl_id: self.impl_.to_chalk(db),
612 value: make_binders(value_bound, self.num_vars),
613 }
614 }
615
616 fn from_chalk(
617 _db: &dyn HirDatabase,
618 _data: AssociatedTyValue,
619 ) -> builtin::BuiltinImplAssocTyValueData {
620 unimplemented!()
621 }
622}
623
624pub(super) fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T>
625where
626 T: HasInterner<Interner = Interner>,
627{
628 chalk_ir::Binders::new(
629 chalk_ir::VariableKinds::from(
630 &Interner,
631 std::iter::repeat(chalk_ir::VariableKind::Ty).take(num_vars),
632 ),
633 value,
634 )
635}
636
637pub(super) fn convert_where_clauses(
638 db: &dyn HirDatabase,
639 def: GenericDefId,
640 substs: &Substs,
641) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
642 let generic_predicates = db.generic_predicates(def);
643 let mut result = Vec::with_capacity(generic_predicates.len());
644 for pred in generic_predicates.iter() {
645 if pred.value.is_error() {
646 // skip errored predicates completely
647 continue;
648 }
649 result.push(pred.clone().subst(substs).to_chalk(db));
650 }
651 result
652}
653
654pub(super) fn generic_predicate_to_inline_bound(
655 db: &dyn HirDatabase,
656 pred: &GenericPredicate,
657 self_ty: &Ty,
658) -> Option<chalk_rust_ir::InlineBound<Interner>> {
659 // An InlineBound is like a GenericPredicate, except the self type is left out.
660 // We don't have a special type for this, but Chalk does.
661 match pred {
662 GenericPredicate::Implemented(trait_ref) => {
663 if &trait_ref.substs[0] != self_ty {
664 // we can only convert predicates back to type bounds if they
665 // have the expected self type
666 return None;
667 }
668 let args_no_self = trait_ref.substs[1..]
669 .iter()
670 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
671 .collect();
672 let trait_bound =
673 chalk_rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self };
674 Some(chalk_rust_ir::InlineBound::TraitBound(trait_bound))
675 }
676 GenericPredicate::Projection(proj) => {
677 if &proj.projection_ty.parameters[0] != self_ty {
678 return None;
679 }
680 let trait_ = match proj.projection_ty.associated_ty.lookup(db.upcast()).container {
681 AssocContainerId::TraitId(t) => t,
682 _ => panic!("associated type not in trait"),
683 };
684 let args_no_self = proj.projection_ty.parameters[1..]
685 .iter()
686 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
687 .collect();
688 let alias_eq_bound = chalk_rust_ir::AliasEqBound {
689 value: proj.ty.clone().to_chalk(db),
690 trait_bound: chalk_rust_ir::TraitBound {
691 trait_id: trait_.to_chalk(db),
692 args_no_self,
693 },
694 associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db),
695 parameters: Vec::new(), // FIXME we don't support generic associated types yet
696 };
697 Some(chalk_rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
698 }
699 GenericPredicate::Error => None,
700 }
701}
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs
index 4867cb17e..d88828c7c 100644
--- a/crates/ra_hir_ty/src/traits/chalk/tls.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs
@@ -1,7 +1,7 @@
1//! Implementation of Chalk debug helper functions using TLS. 1//! Implementation of Chalk debug helper functions using TLS.
2use std::fmt; 2use std::fmt;
3 3
4use chalk_ir::{AliasTy, Goal, Goals, Lifetime, Parameter, ProgramClauseImplication, TypeName}; 4use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication, TypeName};
5use itertools::Itertools; 5use itertools::Itertools;
6 6
7use super::{from_chalk, Interner}; 7use super::{from_chalk, Interner};
@@ -15,10 +15,10 @@ pub struct DebugContext<'a>(&'a (dyn HirDatabase + 'a));
15impl DebugContext<'_> { 15impl DebugContext<'_> {
16 pub fn debug_struct_id( 16 pub fn debug_struct_id(
17 &self, 17 &self,
18 id: super::StructId, 18 id: super::AdtId,
19 f: &mut fmt::Formatter<'_>, 19 f: &mut fmt::Formatter<'_>,
20 ) -> Result<(), fmt::Error> { 20 ) -> Result<(), fmt::Error> {
21 let type_ctor: TypeCtor = from_chalk(self.0, TypeName::Struct(id)); 21 let type_ctor: TypeCtor = from_chalk(self.0, TypeName::Adt(id));
22 match type_ctor { 22 match type_ctor {
23 TypeCtor::Bool => write!(f, "bool")?, 23 TypeCtor::Bool => write!(f, "bool")?,
24 TypeCtor::Char => write!(f, "char")?, 24 TypeCtor::Char => write!(f, "char")?,
@@ -188,9 +188,9 @@ impl DebugContext<'_> {
188 write!(fmt, "{:?}", lifetime.data(&Interner)) 188 write!(fmt, "{:?}", lifetime.data(&Interner))
189 } 189 }
190 190
191 pub fn debug_parameter( 191 pub fn debug_generic_arg(
192 &self, 192 &self,
193 parameter: &Parameter<Interner>, 193 parameter: &GenericArg<Interner>,
194 fmt: &mut fmt::Formatter<'_>, 194 fmt: &mut fmt::Formatter<'_>,
195 ) -> Result<(), fmt::Error> { 195 ) -> Result<(), fmt::Error> {
196 write!(fmt, "{:?}", parameter.data(&Interner).inner_debug()) 196 write!(fmt, "{:?}", parameter.data(&Interner).inner_debug())
@@ -244,6 +244,79 @@ impl DebugContext<'_> {
244 ) -> Result<(), fmt::Error> { 244 ) -> Result<(), fmt::Error> {
245 write!(fmt, "{:?}", separator_trait_ref.debug(&Interner)) 245 write!(fmt, "{:?}", separator_trait_ref.debug(&Interner))
246 } 246 }
247
248 pub fn debug_fn_def_id(
249 &self,
250 fn_def_id: chalk_ir::FnDefId<Interner>,
251 fmt: &mut fmt::Formatter<'_>,
252 ) -> Result<(), fmt::Error> {
253 let def: CallableDef = from_chalk(self.0, fn_def_id);
254 let name = match def {
255 CallableDef::FunctionId(ff) => self.0.function_data(ff).name.clone(),
256 CallableDef::StructId(s) => self.0.struct_data(s).name.clone(),
257 CallableDef::EnumVariantId(e) => {
258 let enum_data = self.0.enum_data(e.parent);
259 enum_data.variants[e.local_id].name.clone()
260 }
261 };
262 match def {
263 CallableDef::FunctionId(_) => write!(fmt, "{{fn {}}}", name),
264 CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => {
265 write!(fmt, "{{ctor {}}}", name)
266 }
267 }
268 }
269
270 pub fn debug_const(
271 &self,
272 _constant: &chalk_ir::Const<Interner>,
273 fmt: &mut fmt::Formatter<'_>,
274 ) -> fmt::Result {
275 write!(fmt, "const")
276 }
277
278 pub fn debug_variable_kinds(
279 &self,
280 variable_kinds: &chalk_ir::VariableKinds<Interner>,
281 fmt: &mut fmt::Formatter<'_>,
282 ) -> fmt::Result {
283 write!(fmt, "{:?}", variable_kinds.as_slice(&Interner))
284 }
285 pub fn debug_variable_kinds_with_angles(
286 &self,
287 variable_kinds: &chalk_ir::VariableKinds<Interner>,
288 fmt: &mut fmt::Formatter<'_>,
289 ) -> fmt::Result {
290 write!(fmt, "{:?}", variable_kinds.inner_debug(&Interner))
291 }
292 pub fn debug_canonical_var_kinds(
293 &self,
294 canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Interner>,
295 fmt: &mut fmt::Formatter<'_>,
296 ) -> fmt::Result {
297 write!(fmt, "{:?}", canonical_var_kinds.as_slice(&Interner))
298 }
299 pub fn debug_program_clause(
300 &self,
301 clause: &chalk_ir::ProgramClause<Interner>,
302 fmt: &mut fmt::Formatter<'_>,
303 ) -> fmt::Result {
304 write!(fmt, "{:?}", clause.data(&Interner))
305 }
306 pub fn debug_program_clauses(
307 &self,
308 clauses: &chalk_ir::ProgramClauses<Interner>,
309 fmt: &mut fmt::Formatter<'_>,
310 ) -> fmt::Result {
311 write!(fmt, "{:?}", clauses.as_slice(&Interner))
312 }
313 pub fn debug_quantified_where_clauses(
314 &self,
315 clauses: &chalk_ir::QuantifiedWhereClauses<Interner>,
316 fmt: &mut fmt::Formatter<'_>,
317 ) -> fmt::Result {
318 write!(fmt, "{:?}", clauses.as_slice(&Interner))
319 }
247} 320}
248 321
249mod unsafe_tls { 322mod unsafe_tls {
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index 6021f7279..cfb7c1e38 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -63,8 +63,8 @@ impl fmt::Debug for CompletionItem {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 let mut s = f.debug_struct("CompletionItem"); 64 let mut s = f.debug_struct("CompletionItem");
65 s.field("label", &self.label()).field("source_range", &self.source_range()); 65 s.field("label", &self.label()).field("source_range", &self.source_range());
66 if self.text_edit().as_indels().len() == 1 { 66 if self.text_edit().len() == 1 {
67 let atom = &self.text_edit().as_indels()[0]; 67 let atom = &self.text_edit().iter().next().unwrap();
68 s.field("delete", &atom.delete); 68 s.field("delete", &atom.delete);
69 s.field("insert", &atom.insert); 69 s.field("insert", &atom.insert);
70 } else { 70 } else {
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 54c2bcc09..3d83c0f71 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -21,7 +21,7 @@ use ra_syntax::{
21}; 21};
22use ra_text_edit::{TextEdit, TextEditBuilder}; 22use ra_text_edit::{TextEdit, TextEditBuilder};
23 23
24use crate::{Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; 24use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceChange, SourceFileEdit};
25 25
26#[derive(Debug, Copy, Clone)] 26#[derive(Debug, Copy, Clone)]
27pub enum Severity { 27pub enum Severity {
@@ -63,8 +63,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
63 .parent() 63 .parent()
64 .unwrap_or_else(|| RelativePath::new("")) 64 .unwrap_or_else(|| RelativePath::new(""))
65 .join(&d.candidate); 65 .join(&d.candidate);
66 let create_file = FileSystemEdit::CreateFile { source_root, path }; 66 let fix =
67 let fix = SourceChange::file_system_edit("Create module", create_file); 67 Fix::new("Create module", FileSystemEdit::CreateFile { source_root, path }.into());
68 res.borrow_mut().push(Diagnostic { 68 res.borrow_mut().push(Diagnostic {
69 range: sema.diagnostics_range(d).range, 69 range: sema.diagnostics_range(d).range,
70 message: d.message(), 70 message: d.message(),
@@ -88,14 +88,12 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
88 field_list = field_list.append_field(&field); 88 field_list = field_list.append_field(&field);
89 } 89 }
90 90
91 let mut builder = TextEditBuilder::default(); 91 let edit = {
92 algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); 92 let mut builder = TextEditBuilder::default();
93 93 algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder);
94 Some(SourceChange::source_file_edit_from( 94 builder.finish()
95 "Fill struct fields", 95 };
96 file_id, 96 Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
97 builder.finish(),
98 ))
99 }; 97 };
100 98
101 res.borrow_mut().push(Diagnostic { 99 res.borrow_mut().push(Diagnostic {
@@ -117,7 +115,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
117 let node = d.ast(db); 115 let node = d.ast(db);
118 let replacement = format!("Ok({})", node.syntax()); 116 let replacement = format!("Ok({})", node.syntax());
119 let edit = TextEdit::replace(node.syntax().text_range(), replacement); 117 let edit = TextEdit::replace(node.syntax().text_range(), replacement);
120 let fix = SourceChange::source_file_edit_from("Wrap with ok", file_id, edit); 118 let source_change = SourceChange::source_file_edit_from(file_id, edit);
119 let fix = Fix::new("Wrap with ok", source_change);
121 res.borrow_mut().push(Diagnostic { 120 res.borrow_mut().push(Diagnostic {
122 range: sema.diagnostics_range(d).range, 121 range: sema.diagnostics_range(d).range,
123 message: d.message(), 122 message: d.message(),
@@ -154,9 +153,9 @@ fn check_unnecessary_braces_in_use_statement(
154 range, 153 range,
155 message: "Unnecessary braces in use statement".to_string(), 154 message: "Unnecessary braces in use statement".to_string(),
156 severity: Severity::WeakWarning, 155 severity: Severity::WeakWarning,
157 fix: Some(SourceChange::source_file_edit( 156 fix: Some(Fix::new(
158 "Remove unnecessary braces", 157 "Remove unnecessary braces",
159 SourceFileEdit { file_id, edit }, 158 SourceFileEdit { file_id, edit }.into(),
160 )), 159 )),
161 }); 160 });
162 } 161 }
@@ -198,9 +197,9 @@ fn check_struct_shorthand_initialization(
198 range: record_field.syntax().text_range(), 197 range: record_field.syntax().text_range(),
199 message: "Shorthand struct initialization".to_string(), 198 message: "Shorthand struct initialization".to_string(),
200 severity: Severity::WeakWarning, 199 severity: Severity::WeakWarning,
201 fix: Some(SourceChange::source_file_edit( 200 fix: Some(Fix::new(
202 "Use struct shorthand initialization", 201 "Use struct shorthand initialization",
203 SourceFileEdit { file_id, edit }, 202 SourceFileEdit { file_id, edit }.into(),
204 )), 203 )),
205 }); 204 });
206 } 205 }
@@ -240,7 +239,7 @@ mod tests {
240 let diagnostic = 239 let diagnostic =
241 diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before)); 240 diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
242 let mut fix = diagnostic.fix.unwrap(); 241 let mut fix = diagnostic.fix.unwrap();
243 let edit = fix.source_file_edits.pop().unwrap().edit; 242 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
244 let actual = { 243 let actual = {
245 let mut actual = before.to_string(); 244 let mut actual = before.to_string();
246 edit.apply(&mut actual); 245 edit.apply(&mut actual);
@@ -258,7 +257,7 @@ mod tests {
258 let (analysis, file_position) = analysis_and_position(fixture); 257 let (analysis, file_position) = analysis_and_position(fixture);
259 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); 258 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap();
260 let mut fix = diagnostic.fix.unwrap(); 259 let mut fix = diagnostic.fix.unwrap();
261 let edit = fix.source_file_edits.pop().unwrap().edit; 260 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
262 let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); 261 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
263 let actual = { 262 let actual = {
264 let mut actual = target_file_contents.to_string(); 263 let mut actual = target_file_contents.to_string();
@@ -295,7 +294,7 @@ mod tests {
295 let (analysis, file_id) = single_file(before); 294 let (analysis, file_id) = single_file(before);
296 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); 295 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap();
297 let mut fix = diagnostic.fix.unwrap(); 296 let mut fix = diagnostic.fix.unwrap();
298 let edit = fix.source_file_edits.pop().unwrap().edit; 297 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
299 let actual = { 298 let actual = {
300 let mut actual = before.to_string(); 299 let mut actual = before.to_string();
301 edit.apply(&mut actual); 300 edit.apply(&mut actual);
@@ -616,23 +615,24 @@ mod tests {
616 Diagnostic { 615 Diagnostic {
617 message: "unresolved module", 616 message: "unresolved module",
618 range: 0..8, 617 range: 0..8,
618 severity: Error,
619 fix: Some( 619 fix: Some(
620 SourceChange { 620 Fix {
621 label: "Create module", 621 label: "Create module",
622 source_file_edits: [], 622 source_change: SourceChange {
623 file_system_edits: [ 623 source_file_edits: [],
624 CreateFile { 624 file_system_edits: [
625 source_root: SourceRootId( 625 CreateFile {
626 0, 626 source_root: SourceRootId(
627 ), 627 0,
628 path: "foo.rs", 628 ),
629 }, 629 path: "foo.rs",
630 ], 630 },
631 cursor_position: None, 631 ],
632 is_snippet: false, 632 is_snippet: false,
633 },
633 }, 634 },
634 ), 635 ),
635 severity: Error,
636 }, 636 },
637 ] 637 ]
638 "###); 638 "###);
@@ -666,30 +666,31 @@ mod tests {
666 Diagnostic { 666 Diagnostic {
667 message: "Missing structure fields:\n- b", 667 message: "Missing structure fields:\n- b",
668 range: 224..233, 668 range: 224..233,
669 severity: Error,
669 fix: Some( 670 fix: Some(
670 SourceChange { 671 Fix {
671 label: "Fill struct fields", 672 label: "Fill struct fields",
672 source_file_edits: [ 673 source_change: SourceChange {
673 SourceFileEdit { 674 source_file_edits: [
674 file_id: FileId( 675 SourceFileEdit {
675 1, 676 file_id: FileId(
676 ), 677 1,
677 edit: TextEdit { 678 ),
678 indels: [ 679 edit: TextEdit {
679 Indel { 680 indels: [
680 insert: "{a:42, b: ()}", 681 Indel {
681 delete: 3..9, 682 insert: "{a:42, b: ()}",
682 }, 683 delete: 3..9,
683 ], 684 },
685 ],
686 },
684 }, 687 },
685 }, 688 ],
686 ], 689 file_system_edits: [],
687 file_system_edits: [], 690 is_snippet: false,
688 cursor_position: None, 691 },
689 is_snippet: false,
690 }, 692 },
691 ), 693 ),
692 severity: Error,
693 }, 694 },
694 ] 695 ]
695 "###); 696 "###);
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index 722092de9..8bb312156 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -79,14 +79,14 @@ pub(crate) fn rust_code_markup_with_doc(
79 doc: Option<&str>, 79 doc: Option<&str>,
80 mod_path: Option<&str>, 80 mod_path: Option<&str>,
81) -> String { 81) -> String {
82 let mut buf = "```rust\n".to_owned(); 82 let mut buf = String::new();
83 83
84 if let Some(mod_path) = mod_path { 84 if let Some(mod_path) = mod_path {
85 if !mod_path.is_empty() { 85 if !mod_path.is_empty() {
86 format_to!(buf, "{}\n", mod_path); 86 format_to!(buf, "{}\n___\n\n", mod_path);
87 } 87 }
88 } 88 }
89 format_to!(buf, "{}\n```", code); 89 format_to!(buf, "```rust\n{}\n```", code);
90 90
91 if let Some(doc) = doc { 91 if let Some(doc) = doc {
92 format_to!(buf, "\n\n{}", doc); 92 format_to!(buf, "\n\n{}", doc);
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index befa977c7..1f4f6b848 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -405,7 +405,7 @@ mod tests {
405 }; 405 };
406 } 406 }
407 "#, 407 "#,
408 &["Foo\nfield_a: u32"], 408 &["Foo\n___\n\n```rust\nfield_a: u32"],
409 ); 409 );
410 410
411 // Hovering over the field in the definition 411 // Hovering over the field in the definition
@@ -422,7 +422,7 @@ mod tests {
422 }; 422 };
423 } 423 }
424 "#, 424 "#,
425 &["Foo\nfield_a: u32"], 425 &["Foo\n___\n\n```rust\nfield_a: u32"],
426 ); 426 );
427 } 427 }
428 428
@@ -475,7 +475,7 @@ fn main() {
475 ", 475 ",
476 ); 476 );
477 let hover = analysis.hover(position).unwrap().unwrap(); 477 let hover = analysis.hover(position).unwrap().unwrap();
478 assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\nSome")); 478 assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n___\n\n```rust\nSome"));
479 479
480 let (analysis, position) = single_file_with_position( 480 let (analysis, position) = single_file_with_position(
481 " 481 "
@@ -503,6 +503,9 @@ fn main() {
503 "#, 503 "#,
504 &[" 504 &["
505Option 505Option
506___
507
508```rust
506None 509None
507``` 510```
508 511
@@ -524,6 +527,9 @@ The None variant
524 "#, 527 "#,
525 &[" 528 &["
526Option 529Option
530___
531
532```rust
527Some 533Some
528``` 534```
529 535
@@ -606,7 +612,10 @@ fn func(foo: i32) { if true { <|>foo; }; }
606 ", 612 ",
607 ); 613 );
608 let hover = analysis.hover(position).unwrap().unwrap(); 614 let hover = analysis.hover(position).unwrap().unwrap();
609 assert_eq!(trim_markup_opt(hover.info.first()), Some("wrapper::Thing\nfn new() -> Thing")); 615 assert_eq!(
616 trim_markup_opt(hover.info.first()),
617 Some("wrapper::Thing\n___\n\n```rust\nfn new() -> Thing")
618 );
610 } 619 }
611 620
612 #[test] 621 #[test]
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs
index d3af780c4..af1ade8a1 100644
--- a/crates/ra_ide/src/join_lines.rs
+++ b/crates/ra_ide/src/join_lines.rs
@@ -166,16 +166,28 @@ fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool {
166 166
167#[cfg(test)] 167#[cfg(test)]
168mod tests { 168mod tests {
169 use crate::test_utils::{assert_eq_text, check_action, extract_range}; 169 use ra_syntax::SourceFile;
170 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
170 171
171 use super::*; 172 use super::*;
172 173
173 fn check_join_lines(before: &str, after: &str) { 174 fn check_join_lines(before: &str, after: &str) {
174 check_action(before, after, |file, offset| { 175 let (before_cursor_pos, before) = extract_offset(before);
175 let range = TextRange::empty(offset); 176 let file = SourceFile::parse(&before).ok().unwrap();
176 let res = join_lines(file, range); 177
177 Some(res) 178 let range = TextRange::empty(before_cursor_pos);
178 }) 179 let result = join_lines(&file, range);
180
181 let actual = {
182 let mut actual = before.to_string();
183 result.apply(&mut actual);
184 actual
185 };
186 let actual_cursor_pos = result
187 .apply_to_offset(before_cursor_pos)
188 .expect("cursor position is affected by the edit");
189 let actual = add_cursor(&actual, actual_cursor_pos);
190 assert_eq_text!(after, &actual);
179 } 191 }
180 192
181 #[test] 193 #[test]
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 83cb498f7..5ac002d82 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -42,9 +42,6 @@ mod inlay_hints;
42mod expand_macro; 42mod expand_macro;
43mod ssr; 43mod ssr;
44 44
45#[cfg(test)]
46mod test_utils;
47
48use std::sync::Arc; 45use std::sync::Arc;
49 46
50use ra_cfg::CfgOptions; 47use ra_cfg::CfgOptions;
@@ -87,12 +84,12 @@ pub use ra_db::{
87pub use ra_ide_db::{ 84pub use ra_ide_db::{
88 change::{AnalysisChange, LibraryData}, 85 change::{AnalysisChange, LibraryData},
89 line_index::{LineCol, LineIndex}, 86 line_index::{LineCol, LineIndex},
90 line_index_utils::translate_offset_with_edit,
91 search::SearchScope, 87 search::SearchScope,
92 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 88 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
93 symbol_index::Query, 89 symbol_index::Query,
94 RootDatabase, 90 RootDatabase,
95}; 91};
92pub use ra_text_edit::{Indel, TextEdit};
96 93
97pub type Cancelable<T> = Result<T, Canceled>; 94pub type Cancelable<T> = Result<T, Canceled>;
98 95
@@ -100,8 +97,22 @@ pub type Cancelable<T> = Result<T, Canceled>;
100pub struct Diagnostic { 97pub struct Diagnostic {
101 pub message: String, 98 pub message: String,
102 pub range: TextRange, 99 pub range: TextRange,
103 pub fix: Option<SourceChange>,
104 pub severity: Severity, 100 pub severity: Severity,
101 pub fix: Option<Fix>,
102}
103
104#[derive(Debug)]
105pub struct Fix {
106 pub label: String,
107 pub source_change: SourceChange,
108}
109
110impl Fix {
111 pub fn new(label: impl Into<String>, source_change: SourceChange) -> Self {
112 let label = label.into();
113 assert!(label.starts_with(char::is_uppercase) && !label.ends_with('.'));
114 Self { label, source_change }
115 }
105} 116}
106 117
107/// Info associated with a text range. 118/// Info associated with a text range.
@@ -289,14 +300,10 @@ impl Analysis {
289 300
290 /// Returns an edit to remove all newlines in the range, cleaning up minor 301 /// Returns an edit to remove all newlines in the range, cleaning up minor
291 /// stuff like trailing commas. 302 /// stuff like trailing commas.
292 pub fn join_lines(&self, frange: FileRange) -> Cancelable<SourceChange> { 303 pub fn join_lines(&self, frange: FileRange) -> Cancelable<TextEdit> {
293 self.with_db(|db| { 304 self.with_db(|db| {
294 let parse = db.parse(frange.file_id); 305 let parse = db.parse(frange.file_id);
295 let file_edit = SourceFileEdit { 306 join_lines::join_lines(&parse.tree(), frange.range)
296 file_id: frange.file_id,
297 edit: join_lines::join_lines(&parse.tree(), frange.range),
298 };
299 SourceChange::source_file_edit("Join lines", file_edit)
300 }) 307 })
301 } 308 }
302 309
@@ -500,7 +507,7 @@ impl Analysis {
500 ) -> Cancelable<Result<SourceChange, SsrError>> { 507 ) -> Cancelable<Result<SourceChange, SsrError>> {
501 self.with_db(|db| { 508 self.with_db(|db| {
502 let edits = ssr::parse_search_replace(query, parse_only, db)?; 509 let edits = ssr::parse_search_replace(query, parse_only, db)?;
503 Ok(SourceChange::source_file_edits("Structural Search Replace", edits)) 510 Ok(SourceChange::source_file_edits(edits))
504 }) 511 })
505 } 512 }
506 513
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 62ec8d85d..28c6349b1 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -128,7 +128,7 @@ fn rename_mod(
128 source_file_edits.extend(ref_edits); 128 source_file_edits.extend(ref_edits);
129 } 129 }
130 130
131 Some(SourceChange::from_edits("Rename", source_file_edits, file_system_edits)) 131 Some(SourceChange::from_edits(source_file_edits, file_system_edits))
132} 132}
133 133
134fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { 134fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> {
@@ -171,7 +171,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo
171 ), 171 ),
172 }); 172 });
173 173
174 Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edits))) 174 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits)))
175} 175}
176 176
177fn text_edit_from_self_param( 177fn text_edit_from_self_param(
@@ -234,7 +234,7 @@ fn rename_self_to_param(
234 let range = ast::SelfParam::cast(self_token.parent()) 234 let range = ast::SelfParam::cast(self_token.parent())
235 .map_or(self_token.text_range(), |p| p.syntax().text_range()); 235 .map_or(self_token.text_range(), |p| p.syntax().text_range());
236 236
237 Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edits))) 237 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits)))
238} 238}
239 239
240fn rename_reference( 240fn rename_reference(
@@ -253,7 +253,7 @@ fn rename_reference(
253 return None; 253 return None;
254 } 254 }
255 255
256 Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edit))) 256 Some(RangeInfo::new(range, SourceChange::source_file_edits(edit)))
257} 257}
258 258
259#[cfg(test)] 259#[cfg(test)]
@@ -642,7 +642,6 @@ mod tests {
642 RangeInfo { 642 RangeInfo {
643 range: 4..7, 643 range: 4..7,
644 info: SourceChange { 644 info: SourceChange {
645 label: "Rename",
646 source_file_edits: [ 645 source_file_edits: [
647 SourceFileEdit { 646 SourceFileEdit {
648 file_id: FileId( 647 file_id: FileId(
@@ -669,7 +668,6 @@ mod tests {
669 dst_path: "bar/foo2.rs", 668 dst_path: "bar/foo2.rs",
670 }, 669 },
671 ], 670 ],
672 cursor_position: None,
673 is_snippet: false, 671 is_snippet: false,
674 }, 672 },
675 }, 673 },
@@ -695,7 +693,6 @@ mod tests {
695 RangeInfo { 693 RangeInfo {
696 range: 4..7, 694 range: 4..7,
697 info: SourceChange { 695 info: SourceChange {
698 label: "Rename",
699 source_file_edits: [ 696 source_file_edits: [
700 SourceFileEdit { 697 SourceFileEdit {
701 file_id: FileId( 698 file_id: FileId(
@@ -722,7 +719,6 @@ mod tests {
722 dst_path: "foo2/mod.rs", 719 dst_path: "foo2/mod.rs",
723 }, 720 },
724 ], 721 ],
725 cursor_position: None,
726 is_snippet: false, 722 is_snippet: false,
727 }, 723 },
728 }, 724 },
@@ -779,7 +775,6 @@ mod tests {
779 RangeInfo { 775 RangeInfo {
780 range: 8..11, 776 range: 8..11,
781 info: SourceChange { 777 info: SourceChange {
782 label: "Rename",
783 source_file_edits: [ 778 source_file_edits: [
784 SourceFileEdit { 779 SourceFileEdit {
785 file_id: FileId( 780 file_id: FileId(
@@ -819,7 +814,6 @@ mod tests {
819 dst_path: "bar/foo2.rs", 814 dst_path: "bar/foo2.rs",
820 }, 815 },
821 ], 816 ],
822 cursor_position: None,
823 is_snippet: false, 817 is_snippet: false,
824 }, 818 },
825 }, 819 },
@@ -986,8 +980,8 @@ mod tests {
986 if let Some(change) = source_change { 980 if let Some(change) = source_change {
987 for edit in change.info.source_file_edits { 981 for edit in change.info.source_file_edits {
988 file_id = Some(edit.file_id); 982 file_id = Some(edit.file_id);
989 for indel in edit.edit.as_indels() { 983 for indel in edit.edit.into_iter() {
990 text_edit_builder.replace(indel.delete, indel.insert.clone()); 984 text_edit_builder.replace(indel.delete, indel.insert);
991 } 985 }
992 } 986 }
993 } 987 }
diff --git a/crates/ra_ide/src/test_utils.rs b/crates/ra_ide/src/test_utils.rs
deleted file mode 100644
index 48c8fd1f4..000000000
--- a/crates/ra_ide/src/test_utils.rs
+++ /dev/null
@@ -1,25 +0,0 @@
1//! FIXME: write short doc here
2
3use ra_syntax::{SourceFile, TextSize};
4use ra_text_edit::TextEdit;
5
6pub use test_utils::*;
7
8pub fn check_action<F: Fn(&SourceFile, TextSize) -> Option<TextEdit>>(
9 before: &str,
10 after: &str,
11 f: F,
12) {
13 let (before_cursor_pos, before) = extract_offset(before);
14 let file = SourceFile::parse(&before).ok().unwrap();
15 let result = f(&file, before_cursor_pos).expect("code action is not applicable");
16 let actual = {
17 let mut actual = before.to_string();
18 result.apply(&mut actual);
19 actual
20 };
21 let actual_cursor_pos =
22 result.apply_to_offset(before_cursor_pos).expect("cursor position is affected by the edit");
23 let actual = add_cursor(&actual, actual_cursor_pos);
24 assert_eq_text!(after, &actual);
25}
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index cd48cad93..39bb3b357 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -17,7 +17,7 @@ mod on_enter;
17 17
18use ra_db::{FilePosition, SourceDatabase}; 18use ra_db::{FilePosition, SourceDatabase};
19use ra_fmt::leading_indent; 19use ra_fmt::leading_indent;
20use ra_ide_db::{source_change::SingleFileChange, RootDatabase}; 20use ra_ide_db::RootDatabase;
21use ra_syntax::{ 21use ra_syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
23 ast::{self, AstToken}, 23 ast::{self, AstToken},
@@ -40,15 +40,11 @@ pub(crate) fn on_char_typed(
40 assert!(TRIGGER_CHARS.contains(char_typed)); 40 assert!(TRIGGER_CHARS.contains(char_typed));
41 let file = &db.parse(position.file_id).tree(); 41 let file = &db.parse(position.file_id).tree();
42 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); 42 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed));
43 let single_file_change = on_char_typed_inner(file, position.offset, char_typed)?; 43 let text_edit = on_char_typed_inner(file, position.offset, char_typed)?;
44 Some(single_file_change.into_source_change(position.file_id)) 44 Some(SourceChange::source_file_edit_from(position.file_id, text_edit))
45} 45}
46 46
47fn on_char_typed_inner( 47fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> {
48 file: &SourceFile,
49 offset: TextSize,
50 char_typed: char,
51) -> Option<SingleFileChange> {
52 assert!(TRIGGER_CHARS.contains(char_typed)); 48 assert!(TRIGGER_CHARS.contains(char_typed));
53 match char_typed { 49 match char_typed {
54 '.' => on_dot_typed(file, offset), 50 '.' => on_dot_typed(file, offset),
@@ -61,7 +57,7 @@ fn on_char_typed_inner(
61/// Returns an edit which should be applied after `=` was typed. Primarily, 57/// Returns an edit which should be applied after `=` was typed. Primarily,
62/// this works when adding `let =`. 58/// this works when adding `let =`.
63// FIXME: use a snippet completion instead of this hack here. 59// FIXME: use a snippet completion instead of this hack here.
64fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { 60fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
65 assert_eq!(file.syntax().text().char_at(offset), Some('=')); 61 assert_eq!(file.syntax().text().char_at(offset), Some('='));
66 let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; 62 let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
67 if let_stmt.semicolon_token().is_some() { 63 if let_stmt.semicolon_token().is_some() {
@@ -79,14 +75,11 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange>
79 return None; 75 return None;
80 } 76 }
81 let offset = let_stmt.syntax().text_range().end(); 77 let offset = let_stmt.syntax().text_range().end();
82 Some(SingleFileChange { 78 Some(TextEdit::insert(offset, ";".to_string()))
83 label: "add semicolon".to_string(),
84 edit: TextEdit::insert(offset, ";".to_string()),
85 })
86} 79}
87 80
88/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. 81/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
89fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { 82fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
90 assert_eq!(file.syntax().text().char_at(offset), Some('.')); 83 assert_eq!(file.syntax().text().char_at(offset), Some('.'));
91 let whitespace = 84 let whitespace =
92 file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; 85 file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;
@@ -107,14 +100,11 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange>
107 return None; 100 return None;
108 } 101 }
109 102
110 Some(SingleFileChange { 103 Some(TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent))
111 label: "reindent dot".to_string(),
112 edit: TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent),
113 })
114} 104}
115 105
116/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` 106/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
117fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { 107fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
118 let file_text = file.syntax().text(); 108 let file_text = file.syntax().text();
119 assert_eq!(file_text.char_at(offset), Some('>')); 109 assert_eq!(file_text.char_at(offset), Some('>'));
120 let after_arrow = offset + TextSize::of('>'); 110 let after_arrow = offset + TextSize::of('>');
@@ -125,10 +115,7 @@ fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChang
125 return None; 115 return None;
126 } 116 }
127 117
128 Some(SingleFileChange { 118 Some(TextEdit::insert(after_arrow, " ".to_string()))
129 label: "add space after return type".to_string(),
130 edit: TextEdit::insert(after_arrow, " ".to_string()),
131 })
132} 119}
133 120
134#[cfg(test)] 121#[cfg(test)]
@@ -144,7 +131,7 @@ mod tests {
144 edit.apply(&mut before); 131 edit.apply(&mut before);
145 let parse = SourceFile::parse(&before); 132 let parse = SourceFile::parse(&before);
146 on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { 133 on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| {
147 it.edit.apply(&mut before); 134 it.apply(&mut before);
148 before.to_string() 135 before.to_string()
149 }) 136 })
150 } 137 }
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs
index 78a40cc94..e7d64b4f6 100644
--- a/crates/ra_ide/src/typing/on_enter.rs
+++ b/crates/ra_ide/src/typing/on_enter.rs
@@ -38,17 +38,12 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour
38 } 38 }
39 39
40 let indent = node_indent(&file, comment.syntax())?; 40 let indent = node_indent(&file, comment.syntax())?;
41 let inserted = format!("\n{}{} ", indent, prefix); 41 let inserted = format!("\n{}{} $0", indent, prefix);
42 let cursor_position = position.offset + TextSize::of(&inserted);
43 let edit = TextEdit::insert(position.offset, inserted); 42 let edit = TextEdit::insert(position.offset, inserted);
44 43
45 Some( 44 let mut res = SourceChange::from(SourceFileEdit { edit, file_id: position.file_id });
46 SourceChange::source_file_edit( 45 res.is_snippet = true;
47 "On enter", 46 Some(res)
48 SourceFileEdit { edit, file_id: position.file_id },
49 )
50 .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }),
51 )
52} 47}
53 48
54fn followed_by_comment(comment: &ast::Comment) -> bool { 49fn followed_by_comment(comment: &ast::Comment) -> bool {
@@ -84,7 +79,7 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
84 79
85#[cfg(test)] 80#[cfg(test)]
86mod tests { 81mod tests {
87 use test_utils::{add_cursor, assert_eq_text, extract_offset}; 82 use test_utils::{assert_eq_text, extract_offset};
88 83
89 use crate::mock_analysis::single_file; 84 use crate::mock_analysis::single_file;
90 85
@@ -98,7 +93,6 @@ mod tests {
98 assert_eq!(result.source_file_edits.len(), 1); 93 assert_eq!(result.source_file_edits.len(), 1);
99 let mut actual = before.to_string(); 94 let mut actual = before.to_string();
100 result.source_file_edits[0].edit.apply(&mut actual); 95 result.source_file_edits[0].edit.apply(&mut actual);
101 let actual = add_cursor(&actual, result.cursor_position.unwrap().offset);
102 Some(actual) 96 Some(actual)
103 } 97 }
104 98
@@ -121,7 +115,7 @@ fn foo() {
121", 115",
122 r" 116 r"
123/// Some docs 117/// Some docs
124/// <|> 118/// $0
125fn foo() { 119fn foo() {
126} 120}
127", 121",
@@ -137,7 +131,7 @@ impl S {
137 r" 131 r"
138impl S { 132impl S {
139 /// Some 133 /// Some
140 /// <|> docs. 134 /// $0 docs.
141 fn foo() {} 135 fn foo() {}
142} 136}
143", 137",
@@ -151,7 +145,7 @@ fn foo() {
151", 145",
152 r" 146 r"
153/// 147///
154/// <|> Some docs 148/// $0 Some docs
155fn foo() { 149fn foo() {
156} 150}
157", 151",
@@ -175,7 +169,7 @@ fn main() {
175 r" 169 r"
176fn main() { 170fn main() {
177 // Fix 171 // Fix
178 // <|> me 172 // $0 me
179 let x = 1 + 1; 173 let x = 1 + 1;
180} 174}
181", 175",
@@ -195,7 +189,7 @@ fn main() {
195 r" 189 r"
196fn main() { 190fn main() {
197 // Fix 191 // Fix
198 // <|> 192 // $0
199 // me 193 // me
200 let x = 1 + 1; 194 let x = 1 + 1;
201} 195}
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index 4f37954bf..1b74e6558 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -3,7 +3,6 @@
3//! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. 3//! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search.
4 4
5pub mod line_index; 5pub mod line_index;
6pub mod line_index_utils;
7pub mod symbol_index; 6pub mod symbol_index;
8pub mod change; 7pub mod change;
9pub mod defs; 8pub mod defs;
diff --git a/crates/ra_ide_db/src/line_index_utils.rs b/crates/ra_ide_db/src/line_index_utils.rs
deleted file mode 100644
index 7fa6fc448..000000000
--- a/crates/ra_ide_db/src/line_index_utils.rs
+++ /dev/null
@@ -1,302 +0,0 @@
1//! Code actions can specify desirable final position of the cursor.
2//!
3//! The position is specified as a `TextSize` in the final file. We need to send
4//! it in `(Line, Column)` coordinate though. However, we only have a LineIndex
5//! for a file pre-edit!
6//!
7//! Code in this module applies this "to (Line, Column) after edit"
8//! transformation.
9
10use std::convert::TryInto;
11
12use ra_syntax::{TextRange, TextSize};
13use ra_text_edit::{Indel, TextEdit};
14
15use crate::line_index::{LineCol, LineIndex, Utf16Char};
16
17pub fn translate_offset_with_edit(
18 line_index: &LineIndex,
19 offset: TextSize,
20 text_edit: &TextEdit,
21) -> LineCol {
22 let mut state = Edits::from_text_edit(&text_edit);
23
24 let mut res = RunningLineCol::new();
25
26 macro_rules! test_step {
27 ($x:ident) => {
28 match &$x {
29 Step::Newline(n) => {
30 if offset < *n {
31 return res.to_line_col(offset);
32 } else {
33 res.add_line(*n);
34 }
35 }
36 Step::Utf16Char(x) => {
37 if offset < x.end() {
38 // if the offset is inside a multibyte char it's invalid
39 // clamp it to the start of the char
40 let clamp = offset.min(x.start());
41 return res.to_line_col(clamp);
42 } else {
43 res.adjust_col(*x);
44 }
45 }
46 }
47 };
48 }
49
50 for orig_step in LineIndexStepIter::from(line_index) {
51 loop {
52 let translated_step = state.translate_step(&orig_step);
53 match state.next_steps(&translated_step) {
54 NextSteps::Use => {
55 test_step!(translated_step);
56 break;
57 }
58 NextSteps::ReplaceMany(ns) => {
59 for n in ns {
60 test_step!(n);
61 }
62 break;
63 }
64 NextSteps::AddMany(ns) => {
65 for n in ns {
66 test_step!(n);
67 }
68 }
69 }
70 }
71 }
72
73 loop {
74 match state.next_inserted_steps() {
75 None => break,
76 Some(ns) => {
77 for n in ns {
78 test_step!(n);
79 }
80 }
81 }
82 }
83
84 res.to_line_col(offset)
85}
86
87#[derive(Debug, Clone)]
88enum Step {
89 Newline(TextSize),
90 Utf16Char(TextRange),
91}
92
93#[derive(Debug)]
94struct LineIndexStepIter<'a> {
95 line_index: &'a LineIndex,
96 next_newline_idx: usize,
97 utf16_chars: Option<(TextSize, std::slice::Iter<'a, Utf16Char>)>,
98}
99
100impl LineIndexStepIter<'_> {
101 fn from(line_index: &LineIndex) -> LineIndexStepIter {
102 let mut x = LineIndexStepIter { line_index, next_newline_idx: 0, utf16_chars: None };
103 // skip first newline since it's not real
104 x.next();
105 x
106 }
107}
108
109impl Iterator for LineIndexStepIter<'_> {
110 type Item = Step;
111 fn next(&mut self) -> Option<Step> {
112 self.utf16_chars
113 .as_mut()
114 .and_then(|(newline, x)| {
115 let x = x.next()?;
116 Some(Step::Utf16Char(TextRange::new(*newline + x.start, *newline + x.end)))
117 })
118 .or_else(|| {
119 let next_newline = *self.line_index.newlines.get(self.next_newline_idx)?;
120 self.utf16_chars = self
121 .line_index
122 .utf16_lines
123 .get(&(self.next_newline_idx as u32))
124 .map(|x| (next_newline, x.iter()));
125 self.next_newline_idx += 1;
126 Some(Step::Newline(next_newline))
127 })
128 }
129}
130
131#[derive(Debug)]
132struct OffsetStepIter<'a> {
133 text: &'a str,
134 offset: TextSize,
135}
136
137impl Iterator for OffsetStepIter<'_> {
138 type Item = Step;
139 fn next(&mut self) -> Option<Step> {
140 let (next, next_offset) = self
141 .text
142 .char_indices()
143 .filter_map(|(i, c)| {
144 let i: TextSize = i.try_into().unwrap();
145 let char_len = TextSize::of(c);
146 if c == '\n' {
147 let next_offset = self.offset + i + char_len;
148 let next = Step::Newline(next_offset);
149 Some((next, next_offset))
150 } else {
151 if !c.is_ascii() {
152 let start = self.offset + i;
153 let end = start + char_len;
154 let next = Step::Utf16Char(TextRange::new(start, end));
155 let next_offset = end;
156 Some((next, next_offset))
157 } else {
158 None
159 }
160 }
161 })
162 .next()?;
163 let next_idx: usize = (next_offset - self.offset).into();
164 self.text = &self.text[next_idx..];
165 self.offset = next_offset;
166 Some(next)
167 }
168}
169
170#[derive(Debug)]
171enum NextSteps<'a> {
172 Use,
173 ReplaceMany(OffsetStepIter<'a>),
174 AddMany(OffsetStepIter<'a>),
175}
176
177#[derive(Debug)]
178struct TranslatedEdit<'a> {
179 delete: TextRange,
180 insert: &'a str,
181 diff: i64,
182}
183
184struct Edits<'a> {
185 edits: &'a [Indel],
186 current: Option<TranslatedEdit<'a>>,
187 acc_diff: i64,
188}
189
190impl<'a> Edits<'a> {
191 fn from_text_edit(text_edit: &'a TextEdit) -> Edits<'a> {
192 let mut x = Edits { edits: text_edit.as_indels(), current: None, acc_diff: 0 };
193 x.advance_edit();
194 x
195 }
196 fn advance_edit(&mut self) {
197 self.acc_diff += self.current.as_ref().map_or(0, |x| x.diff);
198 match self.edits.split_first() {
199 Some((next, rest)) => {
200 let delete = self.translate_range(next.delete);
201 let diff = next.insert.len() as i64 - usize::from(next.delete.len()) as i64;
202 self.current = Some(TranslatedEdit { delete, insert: &next.insert, diff });
203 self.edits = rest;
204 }
205 None => {
206 self.current = None;
207 }
208 }
209 }
210
211 fn next_inserted_steps(&mut self) -> Option<OffsetStepIter<'a>> {
212 let cur = self.current.as_ref()?;
213 let res = Some(OffsetStepIter { offset: cur.delete.start(), text: &cur.insert });
214 self.advance_edit();
215 res
216 }
217
218 fn next_steps(&mut self, step: &Step) -> NextSteps {
219 let step_pos = match *step {
220 Step::Newline(n) => n,
221 Step::Utf16Char(r) => r.end(),
222 };
223 match &mut self.current {
224 Some(edit) => {
225 if step_pos <= edit.delete.start() {
226 NextSteps::Use
227 } else if step_pos <= edit.delete.end() {
228 let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert };
229 // empty slice to avoid returning steps again
230 edit.insert = &edit.insert[edit.insert.len()..];
231 NextSteps::ReplaceMany(iter)
232 } else {
233 let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert };
234 // empty slice to avoid returning steps again
235 edit.insert = &edit.insert[edit.insert.len()..];
236 self.advance_edit();
237 NextSteps::AddMany(iter)
238 }
239 }
240 None => NextSteps::Use,
241 }
242 }
243
244 fn translate_range(&self, range: TextRange) -> TextRange {
245 if self.acc_diff == 0 {
246 range
247 } else {
248 let start = self.translate(range.start());
249 let end = self.translate(range.end());
250 TextRange::new(start, end)
251 }
252 }
253
254 fn translate(&self, x: TextSize) -> TextSize {
255 if self.acc_diff == 0 {
256 x
257 } else {
258 TextSize::from((usize::from(x) as i64 + self.acc_diff) as u32)
259 }
260 }
261
262 fn translate_step(&self, x: &Step) -> Step {
263 if self.acc_diff == 0 {
264 x.clone()
265 } else {
266 match *x {
267 Step::Newline(n) => Step::Newline(self.translate(n)),
268 Step::Utf16Char(r) => Step::Utf16Char(self.translate_range(r)),
269 }
270 }
271 }
272}
273
274#[derive(Debug)]
275struct RunningLineCol {
276 line: u32,
277 last_newline: TextSize,
278 col_adjust: TextSize,
279}
280
281impl RunningLineCol {
282 fn new() -> RunningLineCol {
283 RunningLineCol { line: 0, last_newline: TextSize::from(0), col_adjust: TextSize::from(0) }
284 }
285
286 fn to_line_col(&self, offset: TextSize) -> LineCol {
287 LineCol {
288 line: self.line,
289 col_utf16: ((offset - self.last_newline) - self.col_adjust).into(),
290 }
291 }
292
293 fn add_line(&mut self, newline: TextSize) {
294 self.line += 1;
295 self.last_newline = newline;
296 self.col_adjust = TextSize::from(0);
297 }
298
299 fn adjust_col(&mut self, range: TextRange) {
300 self.col_adjust += range.len() - TextSize::from(1);
301 }
302}
diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs
index 94e118dd8..e713f4b7e 100644
--- a/crates/ra_ide_db/src/source_change.rs
+++ b/crates/ra_ide_db/src/source_change.rs
@@ -3,94 +3,35 @@
3//! 3//!
4//! It can be viewed as a dual for `AnalysisChange`. 4//! It can be viewed as a dual for `AnalysisChange`.
5 5
6use ra_db::{FileId, FilePosition, RelativePathBuf, SourceRootId}; 6use ra_db::{FileId, RelativePathBuf, SourceRootId};
7use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
8 8
9#[derive(Debug, Clone)] 9#[derive(Debug, Clone)]
10pub struct SourceChange { 10pub struct SourceChange {
11 /// For display in the undo log in the editor
12 pub label: String,
13 pub source_file_edits: Vec<SourceFileEdit>, 11 pub source_file_edits: Vec<SourceFileEdit>,
14 pub file_system_edits: Vec<FileSystemEdit>, 12 pub file_system_edits: Vec<FileSystemEdit>,
15 pub cursor_position: Option<FilePosition>,
16 pub is_snippet: bool, 13 pub is_snippet: bool,
17} 14}
18 15
19impl SourceChange { 16impl SourceChange {
20 /// Creates a new SourceChange with the given label 17 /// Creates a new SourceChange with the given label
21 /// from the edits. 18 /// from the edits.
22 pub fn from_edits<L: Into<String>>( 19 pub fn from_edits(
23 label: L,
24 source_file_edits: Vec<SourceFileEdit>, 20 source_file_edits: Vec<SourceFileEdit>,
25 file_system_edits: Vec<FileSystemEdit>, 21 file_system_edits: Vec<FileSystemEdit>,
26 ) -> Self { 22 ) -> Self {
27 SourceChange { 23 SourceChange { source_file_edits, file_system_edits, is_snippet: false }
28 label: label.into(),
29 source_file_edits,
30 file_system_edits,
31 cursor_position: None,
32 is_snippet: false,
33 }
34 } 24 }
35 25
36 /// Creates a new SourceChange with the given label, 26 /// Creates a new SourceChange with the given label,
37 /// containing only the given `SourceFileEdits`. 27 /// containing only the given `SourceFileEdits`.
38 pub fn source_file_edits<L: Into<String>>(label: L, edits: Vec<SourceFileEdit>) -> Self { 28 pub fn source_file_edits(edits: Vec<SourceFileEdit>) -> Self {
39 let label = label.into(); 29 SourceChange { source_file_edits: edits, file_system_edits: vec![], is_snippet: false }
40 assert!(label.starts_with(char::is_uppercase));
41 SourceChange {
42 label: label,
43 source_file_edits: edits,
44 file_system_edits: vec![],
45 cursor_position: None,
46 is_snippet: false,
47 }
48 }
49
50 /// Creates a new SourceChange with the given label,
51 /// containing only the given `FileSystemEdits`.
52 pub(crate) fn file_system_edits<L: Into<String>>(label: L, edits: Vec<FileSystemEdit>) -> Self {
53 SourceChange {
54 label: label.into(),
55 source_file_edits: vec![],
56 file_system_edits: edits,
57 cursor_position: None,
58 is_snippet: false,
59 }
60 }
61
62 /// Creates a new SourceChange with the given label,
63 /// containing only a single `SourceFileEdit`.
64 pub fn source_file_edit<L: Into<String>>(label: L, edit: SourceFileEdit) -> Self {
65 SourceChange::source_file_edits(label, vec![edit])
66 }
67
68 /// Creates a new SourceChange with the given label
69 /// from the given `FileId` and `TextEdit`
70 pub fn source_file_edit_from<L: Into<String>>(
71 label: L,
72 file_id: FileId,
73 edit: TextEdit,
74 ) -> Self {
75 SourceChange::source_file_edit(label, SourceFileEdit { file_id, edit })
76 } 30 }
77
78 /// Creates a new SourceChange with the given label 31 /// Creates a new SourceChange with the given label
79 /// from the given `FileId` and `TextEdit` 32 /// from the given `FileId` and `TextEdit`
80 pub fn file_system_edit<L: Into<String>>(label: L, edit: FileSystemEdit) -> Self { 33 pub fn source_file_edit_from(file_id: FileId, edit: TextEdit) -> Self {
81 SourceChange::file_system_edits(label, vec![edit]) 34 SourceFileEdit { file_id, edit }.into()
82 }
83
84 /// Sets the cursor position to the given `FilePosition`
85 pub fn with_cursor(mut self, cursor_position: FilePosition) -> Self {
86 self.cursor_position = Some(cursor_position);
87 self
88 }
89
90 /// Sets the cursor position to the given `FilePosition`
91 pub fn with_cursor_opt(mut self, cursor_position: Option<FilePosition>) -> Self {
92 self.cursor_position = cursor_position;
93 self
94 } 35 }
95} 36}
96 37
@@ -100,24 +41,27 @@ pub struct SourceFileEdit {
100 pub edit: TextEdit, 41 pub edit: TextEdit,
101} 42}
102 43
44impl From<SourceFileEdit> for SourceChange {
45 fn from(edit: SourceFileEdit) -> SourceChange {
46 SourceChange {
47 source_file_edits: vec![edit],
48 file_system_edits: Vec::new(),
49 is_snippet: false,
50 }
51 }
52}
53
103#[derive(Debug, Clone)] 54#[derive(Debug, Clone)]
104pub enum FileSystemEdit { 55pub enum FileSystemEdit {
105 CreateFile { source_root: SourceRootId, path: RelativePathBuf }, 56 CreateFile { source_root: SourceRootId, path: RelativePathBuf },
106 MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf }, 57 MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf },
107} 58}
108 59
109pub struct SingleFileChange { 60impl From<FileSystemEdit> for SourceChange {
110 pub label: String, 61 fn from(edit: FileSystemEdit) -> SourceChange {
111 pub edit: TextEdit,
112}
113
114impl SingleFileChange {
115 pub fn into_source_change(self, file_id: FileId) -> SourceChange {
116 SourceChange { 62 SourceChange {
117 label: self.label, 63 source_file_edits: Vec::new(),
118 source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }], 64 file_system_edits: vec![edit],
119 file_system_edits: Vec::new(),
120 cursor_position: None,
121 is_snippet: false, 65 is_snippet: false,
122 } 66 }
123 } 67 }
diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs
index c4f945101..25554f583 100644
--- a/crates/ra_text_edit/src/lib.rs
+++ b/crates/ra_text_edit/src/lib.rs
@@ -3,6 +3,7 @@
3//! `rust-analyzer` never mutates text itself and only sends diffs to clients, 3//! `rust-analyzer` never mutates text itself and only sends diffs to clients,
4//! so `TextEdit` is the ultimate representation of the work done by 4//! so `TextEdit` is the ultimate representation of the work done by
5//! rust-analyzer. 5//! rust-analyzer.
6use std::{slice, vec};
6 7
7pub use text_size::{TextRange, TextSize}; 8pub use text_size::{TextRange, TextSize};
8 9
@@ -16,7 +17,7 @@ pub struct Indel {
16 pub delete: TextRange, 17 pub delete: TextRange,
17} 18}
18 19
19#[derive(Debug, Clone)] 20#[derive(Default, Debug, Clone)]
20pub struct TextEdit { 21pub struct TextEdit {
21 indels: Vec<Indel>, 22 indels: Vec<Indel>,
22} 23}
@@ -63,25 +64,24 @@ impl TextEdit {
63 builder.finish() 64 builder.finish()
64 } 65 }
65 66
66 pub(crate) fn from_indels(mut indels: Vec<Indel>) -> TextEdit { 67 pub fn len(&self) -> usize {
67 indels.sort_by_key(|a| (a.delete.start(), a.delete.end())); 68 self.indels.len()
68 for (a1, a2) in indels.iter().zip(indels.iter().skip(1)) {
69 assert!(a1.delete.end() <= a2.delete.start())
70 }
71 TextEdit { indels }
72 } 69 }
73 70
74 pub fn is_empty(&self) -> bool { 71 pub fn is_empty(&self) -> bool {
75 self.indels.is_empty() 72 self.indels.is_empty()
76 } 73 }
77 74
78 // FXME: impl IntoIter instead 75 pub fn iter(&self) -> slice::Iter<'_, Indel> {
79 pub fn as_indels(&self) -> &[Indel] { 76 self.indels.iter()
80 &self.indels 77 }
78
79 pub fn into_iter(self) -> vec::IntoIter<Indel> {
80 self.indels.into_iter()
81 } 81 }
82 82
83 pub fn apply(&self, text: &mut String) { 83 pub fn apply(&self, text: &mut String) {
84 match self.indels.len() { 84 match self.len() {
85 0 => return, 85 0 => return,
86 1 => { 86 1 => {
87 self.indels[0].apply(text); 87 self.indels[0].apply(text);
@@ -114,6 +114,17 @@ impl TextEdit {
114 *text = buf 114 *text = buf
115 } 115 }
116 116
117 pub fn union(&mut self, other: TextEdit) -> Result<(), TextEdit> {
118 // FIXME: can be done without allocating intermediate vector
119 let mut all = self.iter().chain(other.iter()).collect::<Vec<_>>();
120 if !check_disjoint(&mut all) {
121 return Err(other);
122 }
123 self.indels.extend(other.indels);
124 assert!(check_disjoint(&mut self.indels));
125 Ok(())
126 }
127
117 pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> { 128 pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> {
118 let mut res = offset; 129 let mut res = offset;
119 for indel in self.indels.iter() { 130 for indel in self.indels.iter() {
@@ -141,9 +152,19 @@ impl TextEditBuilder {
141 self.indels.push(Indel::insert(offset, text)) 152 self.indels.push(Indel::insert(offset, text))
142 } 153 }
143 pub fn finish(self) -> TextEdit { 154 pub fn finish(self) -> TextEdit {
144 TextEdit::from_indels(self.indels) 155 let mut indels = self.indels;
156 assert!(check_disjoint(&mut indels));
157 TextEdit { indels }
145 } 158 }
146 pub fn invalidates_offset(&self, offset: TextSize) -> bool { 159 pub fn invalidates_offset(&self, offset: TextSize) -> bool {
147 self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset)) 160 self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset))
148 } 161 }
149} 162}
163
164fn check_disjoint(indels: &mut [impl std::borrow::Borrow<Indel>]) -> bool {
165 indels.sort_by_key(|indel| (indel.borrow().delete.start(), indel.borrow().delete.end()));
166 indels
167 .iter()
168 .zip(indels.iter().skip(1))
169 .all(|(l, r)| l.borrow().delete.end() <= r.borrow().delete.start())
170}
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 09908458d..e82fd57de 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -74,12 +74,25 @@ fn run_server() -> Result<()> {
74 log::info!("lifecycle: server started"); 74 log::info!("lifecycle: server started");
75 75
76 let (connection, io_threads) = Connection::stdio(); 76 let (connection, io_threads) = Connection::stdio();
77 let server_capabilities = serde_json::to_value(rust_analyzer::server_capabilities()).unwrap();
78 77
79 let initialize_params = connection.initialize(server_capabilities)?; 78 let (initialize_id, initialize_params) = connection.initialize_start()?;
80 let initialize_params = 79 let initialize_params =
81 from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?; 80 from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;
82 81
82 let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities);
83
84 let initialize_result = lsp_types::InitializeResult {
85 capabilities: server_capabilities,
86 server_info: Some(lsp_types::ServerInfo {
87 name: String::from("rust-analyzer"),
88 version: Some(String::from(env!("REV"))),
89 }),
90 };
91
92 let initialize_result = serde_json::to_value(initialize_result).unwrap();
93
94 connection.initialize_finish(initialize_id, initialize_result)?;
95
83 if let Some(client_info) = initialize_params.client_info { 96 if let Some(client_info) = initialize_params.client_info {
84 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); 97 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
85 } 98 }
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 110c9a442..780fc9317 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -1,19 +1,23 @@
1//! Advertizes the capabilities of the LSP Server. 1//! Advertizes the capabilities of the LSP Server.
2use std::env; 2use std::env;
3 3
4use crate::semantic_tokens;
5
6use lsp_types::{ 4use lsp_types::{
7 CallHierarchyServerCapability, CodeActionOptions, CodeActionProviderCapability, 5 CallHierarchyServerCapability, ClientCapabilities, CodeActionOptions,
8 CodeLensOptions, CompletionOptions, DocumentOnTypeFormattingOptions, 6 CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
9 FoldingRangeProviderCapability, ImplementationProviderCapability, RenameOptions, 7 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
10 RenameProviderCapability, SaveOptions, SelectionRangeProviderCapability, 8 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
11 SemanticTokensDocumentProvider, SemanticTokensLegend, SemanticTokensOptions, 9 SelectionRangeProviderCapability, SemanticTokensDocumentProvider, SemanticTokensLegend,
12 ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, 10 SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
13 TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions, 11 TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
12 WorkDoneProgressOptions,
14}; 13};
14use serde_json::json;
15
16use crate::semantic_tokens;
17
18pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities {
19 let code_action_provider = code_action_capabilities(client_caps);
15 20
16pub fn server_capabilities() -> ServerCapabilities {
17 ServerCapabilities { 21 ServerCapabilities {
18 text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { 22 text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
19 open_close: Some(true), 23 open_close: Some(true),
@@ -45,20 +49,7 @@ pub fn server_capabilities() -> ServerCapabilities {
45 document_highlight_provider: Some(true), 49 document_highlight_provider: Some(true),
46 document_symbol_provider: Some(true), 50 document_symbol_provider: Some(true),
47 workspace_symbol_provider: Some(true), 51 workspace_symbol_provider: Some(true),
48 code_action_provider: Some(CodeActionProviderCapability::Options(CodeActionOptions { 52 code_action_provider: Some(code_action_provider),
49 // Advertise support for all built-in CodeActionKinds
50 code_action_kinds: Some(vec![
51 lsp_types::code_action_kind::EMPTY.to_string(),
52 lsp_types::code_action_kind::QUICKFIX.to_string(),
53 lsp_types::code_action_kind::REFACTOR.to_string(),
54 lsp_types::code_action_kind::REFACTOR_EXTRACT.to_string(),
55 lsp_types::code_action_kind::REFACTOR_INLINE.to_string(),
56 lsp_types::code_action_kind::REFACTOR_REWRITE.to_string(),
57 lsp_types::code_action_kind::SOURCE.to_string(),
58 lsp_types::code_action_kind::SOURCE_ORGANIZE_IMPORTS.to_string(),
59 ]),
60 work_done_progress_options: Default::default(),
61 })),
62 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), 53 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
63 document_formatting_provider: Some(true), 54 document_formatting_provider: Some(true),
64 document_range_formatting_provider: None, 55 document_range_formatting_provider: None,
@@ -91,6 +82,35 @@ pub fn server_capabilities() -> ServerCapabilities {
91 } 82 }
92 .into(), 83 .into(),
93 ), 84 ),
94 experimental: Default::default(), 85 experimental: Some(json!({
86 "joinLines": true,
87 "ssr": true,
88 })),
95 } 89 }
96} 90}
91
92fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProviderCapability {
93 client_caps
94 .text_document
95 .as_ref()
96 .and_then(|it| it.code_action.as_ref())
97 .and_then(|it| it.code_action_literal_support.as_ref())
98 .map_or(CodeActionProviderCapability::Simple(true), |_| {
99 CodeActionProviderCapability::Options(CodeActionOptions {
100 // Advertise support for all built-in CodeActionKinds.
101 // Ideally we would base this off of the client capabilities
102 // but the client is supposed to fall back gracefully for unknown values.
103 code_action_kinds: Some(vec![
104 lsp_types::code_action_kind::EMPTY.to_string(),
105 lsp_types::code_action_kind::QUICKFIX.to_string(),
106 lsp_types::code_action_kind::REFACTOR.to_string(),
107 lsp_types::code_action_kind::REFACTOR_EXTRACT.to_string(),
108 lsp_types::code_action_kind::REFACTOR_INLINE.to_string(),
109 lsp_types::code_action_kind::REFACTOR_REWRITE.to_string(),
110 lsp_types::code_action_kind::SOURCE.to_string(),
111 lsp_types::code_action_kind::SOURCE_ORGANIZE_IMPORTS.to_string(),
112 ]),
113 work_done_progress_options: Default::default(),
114 })
115 })
116}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index d75c48597..c0f7c2c0c 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -102,6 +102,7 @@ pub struct ClientCapsConfig {
102 pub hierarchical_symbols: bool, 102 pub hierarchical_symbols: bool,
103 pub code_action_literals: bool, 103 pub code_action_literals: bool,
104 pub work_done_progress: bool, 104 pub work_done_progress: bool,
105 pub code_action_group: bool,
105} 106}
106 107
107impl Default for Config { 108impl Default for Config {
@@ -268,10 +269,8 @@ impl Config {
268 { 269 {
269 self.client_caps.hierarchical_symbols = value 270 self.client_caps.hierarchical_symbols = value
270 } 271 }
271 if let Some(value) = doc_caps 272 if let Some(value) =
272 .code_action 273 doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some())
273 .as_ref()
274 .and_then(|it| Some(it.code_action_literal_support.is_some()))
275 { 274 {
276 self.client_caps.code_action_literals = value; 275 self.client_caps.code_action_literals = value;
277 } 276 }
@@ -294,9 +293,13 @@ impl Config {
294 293
295 self.assist.allow_snippets(false); 294 self.assist.allow_snippets(false);
296 if let Some(experimental) = &caps.experimental { 295 if let Some(experimental) = &caps.experimental {
297 let enable = 296 let snippet_text_edit =
298 experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); 297 experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true);
299 self.assist.allow_snippets(enable); 298 self.assist.allow_snippets(snippet_text_edit);
299
300 let code_action_group =
301 experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true);
302 self.client_caps.code_action_group = code_action_group
300 } 303 }
301 } 304 }
302} 305}
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
index 96466b5c9..c40cfdcdc 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
@@ -65,6 +65,7 @@ expression: diag
65 fixes: [ 65 fixes: [
66 CodeAction { 66 CodeAction {
67 title: "return the expression directly", 67 title: "return the expression directly",
68 group: None,
68 kind: Some( 69 kind: Some(
69 "quickfix", 70 "quickfix",
70 ), 71 ),
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
index 8f962277f..6dd3fcb2e 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
@@ -50,6 +50,7 @@ expression: diag
50 fixes: [ 50 fixes: [
51 CodeAction { 51 CodeAction {
52 title: "consider prefixing with an underscore", 52 title: "consider prefixing with an underscore",
53 group: None,
53 kind: Some( 54 kind: Some(
54 "quickfix", 55 "quickfix",
55 ), 56 ),
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index afea59525..a500d670a 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -145,6 +145,7 @@ fn map_rust_child_diagnostic(
145 } else { 145 } else {
146 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { 146 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
147 title: rd.message.clone(), 147 title: rd.message.clone(),
148 group: None,
148 kind: Some("quickfix".to_string()), 149 kind: Some("quickfix".to_string()),
149 edit: Some(lsp_ext::SnippetWorkspaceEdit { 150 edit: Some(lsp_ext::SnippetWorkspaceEdit {
150 // FIXME: there's no good reason to use edit_map here.... 151 // FIXME: there's no good reason to use edit_map here....
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index f75a26eb7..c25d90a50 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -87,22 +87,22 @@ pub enum JoinLines {}
87 87
88impl Request for JoinLines { 88impl Request for JoinLines {
89 type Params = JoinLinesParams; 89 type Params = JoinLinesParams;
90 type Result = SourceChange; 90 type Result = Vec<lsp_types::TextEdit>;
91 const METHOD: &'static str = "rust-analyzer/joinLines"; 91 const METHOD: &'static str = "experimental/joinLines";
92} 92}
93 93
94#[derive(Deserialize, Serialize, Debug)] 94#[derive(Deserialize, Serialize, Debug)]
95#[serde(rename_all = "camelCase")] 95#[serde(rename_all = "camelCase")]
96pub struct JoinLinesParams { 96pub struct JoinLinesParams {
97 pub text_document: TextDocumentIdentifier, 97 pub text_document: TextDocumentIdentifier,
98 pub range: Range, 98 pub ranges: Vec<Range>,
99} 99}
100 100
101pub enum OnEnter {} 101pub enum OnEnter {}
102 102
103impl Request for OnEnter { 103impl Request for OnEnter {
104 type Params = lsp_types::TextDocumentPositionParams; 104 type Params = lsp_types::TextDocumentPositionParams;
105 type Result = Option<SourceChange>; 105 type Result = Option<SnippetWorkspaceEdit>;
106 const METHOD: &'static str = "rust-analyzer/onEnter"; 106 const METHOD: &'static str = "rust-analyzer/onEnter";
107} 107}
108 108
@@ -133,14 +133,6 @@ pub struct Runnable {
133 pub cwd: Option<PathBuf>, 133 pub cwd: Option<PathBuf>,
134} 134}
135 135
136#[derive(Deserialize, Serialize, Debug)]
137#[serde(rename_all = "camelCase")]
138pub struct SourceChange {
139 pub label: String,
140 pub workspace_edit: SnippetWorkspaceEdit,
141 pub cursor_position: Option<lsp_types::TextDocumentPositionParams>,
142}
143
144pub enum InlayHints {} 136pub enum InlayHints {}
145 137
146impl Request for InlayHints { 138impl Request for InlayHints {
@@ -173,8 +165,8 @@ pub enum Ssr {}
173 165
174impl Request for Ssr { 166impl Request for Ssr {
175 type Params = SsrParams; 167 type Params = SsrParams;
176 type Result = SourceChange; 168 type Result = lsp_types::WorkspaceEdit;
177 const METHOD: &'static str = "rust-analyzer/ssr"; 169 const METHOD: &'static str = "experimental/ssr";
178} 170}
179 171
180#[derive(Debug, Deserialize, Serialize)] 172#[derive(Debug, Deserialize, Serialize)]
@@ -196,6 +188,8 @@ impl Request for CodeActionRequest {
196pub struct CodeAction { 188pub struct CodeAction {
197 pub title: String, 189 pub title: String,
198 #[serde(skip_serializing_if = "Option::is_none")] 190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub group: Option<String>,
192 #[serde(skip_serializing_if = "Option::is_none")]
199 pub kind: Option<String>, 193 pub kind: Option<String>,
200 #[serde(skip_serializing_if = "Option::is_none")] 194 #[serde(skip_serializing_if = "Option::is_none")]
201 pub command: Option<lsp_types::Command>, 195 pub command: Option<lsp_types::Command>,
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index be6a0aece..ba6857556 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -15,10 +15,11 @@ use lsp_types::{
15 DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, 15 DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location,
16 MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, 16 MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams,
17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, 17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit, 18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit,
19}; 19};
20use ra_ide::{ 20use ra_ide::{
21 Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, 21 FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
22 TextEdit,
22}; 23};
23use ra_prof::profile; 24use ra_prof::profile;
24use ra_project_model::TargetKind; 25use ra_project_model::TargetKind;
@@ -149,22 +150,35 @@ pub fn handle_find_matching_brace(
149pub fn handle_join_lines( 150pub fn handle_join_lines(
150 world: WorldSnapshot, 151 world: WorldSnapshot,
151 params: lsp_ext::JoinLinesParams, 152 params: lsp_ext::JoinLinesParams,
152) -> Result<lsp_ext::SourceChange> { 153) -> Result<Vec<lsp_types::TextEdit>> {
153 let _p = profile("handle_join_lines"); 154 let _p = profile("handle_join_lines");
154 let frange = from_proto::file_range(&world, params.text_document, params.range)?; 155 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
155 let source_change = world.analysis().join_lines(frange)?; 156 let line_index = world.analysis().file_line_index(file_id)?;
156 to_proto::source_change(&world, source_change) 157 let line_endings = world.file_line_endings(file_id);
158 let mut res = TextEdit::default();
159 for range in params.ranges {
160 let range = from_proto::text_range(&line_index, range);
161 let edit = world.analysis().join_lines(FileRange { file_id, range })?;
162 match res.union(edit) {
163 Ok(()) => (),
164 Err(_edit) => {
165 // just ignore overlapping edits
166 }
167 }
168 }
169 let res = to_proto::text_edit_vec(&line_index, line_endings, res);
170 Ok(res)
157} 171}
158 172
159pub fn handle_on_enter( 173pub fn handle_on_enter(
160 world: WorldSnapshot, 174 world: WorldSnapshot,
161 params: lsp_types::TextDocumentPositionParams, 175 params: lsp_types::TextDocumentPositionParams,
162) -> Result<Option<lsp_ext::SourceChange>> { 176) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> {
163 let _p = profile("handle_on_enter"); 177 let _p = profile("handle_on_enter");
164 let position = from_proto::file_position(&world, params)?; 178 let position = from_proto::file_position(&world, params)?;
165 match world.analysis().on_enter(position)? { 179 match world.analysis().on_enter(position)? {
166 None => Ok(None), 180 None => Ok(None),
167 Some(source_change) => to_proto::source_change(&world, source_change).map(Some), 181 Some(source_change) => to_proto::snippet_workspace_edit(&world, source_change).map(Some),
168 } 182 }
169} 183}
170 184
@@ -172,7 +186,7 @@ pub fn handle_on_enter(
172pub fn handle_on_type_formatting( 186pub fn handle_on_type_formatting(
173 world: WorldSnapshot, 187 world: WorldSnapshot,
174 params: lsp_types::DocumentOnTypeFormattingParams, 188 params: lsp_types::DocumentOnTypeFormattingParams,
175) -> Result<Option<Vec<TextEdit>>> { 189) -> Result<Option<Vec<lsp_types::TextEdit>>> {
176 let _p = profile("handle_on_type_formatting"); 190 let _p = profile("handle_on_type_formatting");
177 let mut position = from_proto::file_position(&world, params.text_document_position)?; 191 let mut position = from_proto::file_position(&world, params.text_document_position)?;
178 let line_index = world.analysis().file_line_index(position.file_id)?; 192 let line_index = world.analysis().file_line_index(position.file_id)?;
@@ -267,7 +281,7 @@ pub fn handle_document_symbol(
267 kind: symbol.kind, 281 kind: symbol.kind,
268 deprecated: symbol.deprecated, 282 deprecated: symbol.deprecated,
269 location: Location::new(url.clone(), symbol.range), 283 location: Location::new(url.clone(), symbol.range),
270 container_name: container_name, 284 container_name,
271 }); 285 });
272 286
273 for child in symbol.children.iter().flatten() { 287 for child in symbol.children.iter().flatten() {
@@ -618,7 +632,7 @@ pub fn handle_references(
618pub fn handle_formatting( 632pub fn handle_formatting(
619 world: WorldSnapshot, 633 world: WorldSnapshot,
620 params: DocumentFormattingParams, 634 params: DocumentFormattingParams,
621) -> Result<Option<Vec<TextEdit>>> { 635) -> Result<Option<Vec<lsp_types::TextEdit>>> {
622 let _p = profile("handle_formatting"); 636 let _p = profile("handle_formatting");
623 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 637 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
624 let file = world.analysis().file_text(file_id)?; 638 let file = world.analysis().file_text(file_id)?;
@@ -685,7 +699,7 @@ pub fn handle_formatting(
685 } 699 }
686 } 700 }
687 701
688 Ok(Some(vec![TextEdit { 702 Ok(Some(vec![lsp_types::TextEdit {
689 range: Range::new(Position::new(0, 0), end_position), 703 range: Range::new(Position::new(0, 0), end_position),
690 new_text: captured_stdout, 704 new_text: captured_stdout,
691 }])) 705 }]))
@@ -706,6 +720,7 @@ pub fn handle_code_action(
706 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 720 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
707 let line_index = world.analysis().file_line_index(file_id)?; 721 let line_index = world.analysis().file_line_index(file_id)?;
708 let range = from_proto::text_range(&line_index, params.range); 722 let range = from_proto::text_range(&line_index, params.range);
723 let frange = FileRange { file_id, range };
709 724
710 let diagnostics = world.analysis().diagnostics(file_id)?; 725 let diagnostics = world.analysis().diagnostics(file_id)?;
711 let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); 726 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
@@ -716,10 +731,11 @@ pub fn handle_code_action(
716 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) 731 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some())
717 .map(|(_range, fix)| fix); 732 .map(|(_range, fix)| fix);
718 733
719 for source_edit in fixes_from_diagnostics { 734 for fix in fixes_from_diagnostics {
720 let title = source_edit.label.clone(); 735 let title = fix.label;
721 let edit = to_proto::snippet_workspace_edit(&world, source_edit)?; 736 let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?;
722 let action = lsp_ext::CodeAction { title, kind: None, edit: Some(edit), command: None }; 737 let action =
738 lsp_ext::CodeAction { title, group: None, kind: None, edit: Some(edit), command: None };
723 res.push(action); 739 res.push(action);
724 } 740 }
725 741
@@ -731,53 +747,9 @@ pub fn handle_code_action(
731 res.push(fix.action.clone()); 747 res.push(fix.action.clone());
732 } 748 }
733 749
734 let mut grouped_assists: FxHashMap<String, (usize, Vec<Assist>)> = FxHashMap::default(); 750 for assist in world.analysis().assists(&world.config.assist, frange)?.into_iter() {
735 for assist in 751 res.push(to_proto::code_action(&world, assist)?.into());
736 world.analysis().assists(&world.config.assist, FileRange { file_id, range })?.into_iter()
737 {
738 match &assist.group_label {
739 Some(label) => grouped_assists
740 .entry(label.to_owned())
741 .or_insert_with(|| {
742 let idx = res.len();
743 let dummy = lsp_ext::CodeAction {
744 title: String::new(),
745 kind: None,
746 command: None,
747 edit: None,
748 };
749 res.push(dummy);
750 (idx, Vec::new())
751 })
752 .1
753 .push(assist),
754 None => {
755 res.push(to_proto::code_action(&world, assist)?.into());
756 }
757 }
758 }
759
760 for (group_label, (idx, assists)) in grouped_assists {
761 if assists.len() == 1 {
762 res[idx] = to_proto::code_action(&world, assists.into_iter().next().unwrap())?.into();
763 } else {
764 let title = group_label;
765
766 let mut arguments = Vec::with_capacity(assists.len());
767 for assist in assists {
768 let source_change = to_proto::source_change(&world, assist.source_change)?;
769 arguments.push(to_value(source_change)?);
770 }
771
772 let command = Some(Command {
773 title: title.clone(),
774 command: "rust-analyzer.selectAndApplySourceChange".to_string(),
775 arguments: Some(vec![serde_json::Value::Array(arguments)]),
776 });
777 res[idx] = lsp_ext::CodeAction { title, kind: None, edit: None, command };
778 }
779 } 752 }
780
781 Ok(Some(res)) 753 Ok(Some(res))
782} 754}
783 755
@@ -972,11 +944,11 @@ pub fn handle_document_highlight(
972pub fn handle_ssr( 944pub fn handle_ssr(
973 world: WorldSnapshot, 945 world: WorldSnapshot,
974 params: lsp_ext::SsrParams, 946 params: lsp_ext::SsrParams,
975) -> Result<lsp_ext::SourceChange> { 947) -> Result<lsp_types::WorkspaceEdit> {
976 let _p = profile("handle_ssr"); 948 let _p = profile("handle_ssr");
977 let source_change = 949 let source_change =
978 world.analysis().structural_search_replace(&params.query, params.parse_only)??; 950 world.analysis().structural_search_replace(&params.query, params.parse_only)??;
979 to_proto::source_change(&world, source_change) 951 to_proto::workspace_edit(&world, source_change)
980} 952}
981 953
982pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { 954pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> {
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index af54f81b7..7dd7d9416 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -1,13 +1,12 @@
1//! Conversion of rust-analyzer specific types to lsp_types equivalents. 1//! Conversion of rust-analyzer specific types to lsp_types equivalents.
2use ra_db::{FileId, FileRange}; 2use ra_db::{FileId, FileRange};
3use ra_ide::{ 3use ra_ide::{
4 translate_offset_with_edit, Assist, CompletionItem, CompletionItemKind, Documentation, 4 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind,
5 FileSystemEdit, Fold, FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, 5 FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel,
6 HighlightedRange, InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, 6 InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity,
7 ReferenceAccess, Severity, SourceChange, SourceFileEdit, 7 SourceChange, SourceFileEdit, TextEdit,
8}; 8};
9use ra_syntax::{SyntaxKind, TextRange, TextSize}; 9use ra_syntax::{SyntaxKind, TextRange, TextSize};
10use ra_text_edit::{Indel, TextEdit};
11use ra_vfs::LineEndings; 10use ra_vfs::LineEndings;
12 11
13use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; 12use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result};
@@ -133,11 +132,7 @@ pub(crate) fn text_edit_vec(
133 line_endings: LineEndings, 132 line_endings: LineEndings,
134 text_edit: TextEdit, 133 text_edit: TextEdit,
135) -> Vec<lsp_types::TextEdit> { 134) -> Vec<lsp_types::TextEdit> {
136 text_edit 135 text_edit.into_iter().map(|indel| self::text_edit(line_index, line_endings, indel)).collect()
137 .as_indels()
138 .iter()
139 .map(|it| self::text_edit(line_index, line_endings, it.clone()))
140 .collect()
141} 136}
142 137
143pub(crate) fn completion_item( 138pub(crate) fn completion_item(
@@ -150,7 +145,7 @@ pub(crate) fn completion_item(
150 // LSP does not allow arbitrary edits in completion, so we have to do a 145 // LSP does not allow arbitrary edits in completion, so we have to do a
151 // non-trivial mapping here. 146 // non-trivial mapping here.
152 let source_range = completion_item.source_range(); 147 let source_range = completion_item.source_range();
153 for indel in completion_item.text_edit().as_indels() { 148 for indel in completion_item.text_edit().iter() {
154 if indel.delete.contains_range(source_range) { 149 if indel.delete.contains_range(source_range) {
155 text_edit = Some(if indel.delete == source_range { 150 text_edit = Some(if indel.delete == source_range {
156 self::text_edit(line_index, line_endings, indel.clone()) 151 self::text_edit(line_index, line_endings, indel.clone())
@@ -375,14 +370,6 @@ pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result<lsp_types::U
375 world.file_id_to_uri(file_id) 370 world.file_id_to_uri(file_id)
376} 371}
377 372
378pub(crate) fn text_document_identifier(
379 world: &WorldSnapshot,
380 file_id: FileId,
381) -> Result<lsp_types::TextDocumentIdentifier> {
382 let res = lsp_types::TextDocumentIdentifier { uri: url(world, file_id)? };
383 Ok(res)
384}
385
386pub(crate) fn versioned_text_document_identifier( 373pub(crate) fn versioned_text_document_identifier(
387 world: &WorldSnapshot, 374 world: &WorldSnapshot,
388 file_id: FileId, 375 file_id: FileId,
@@ -467,9 +454,8 @@ pub(crate) fn snippet_text_document_edit(
467 let line_endings = world.file_line_endings(source_file_edit.file_id); 454 let line_endings = world.file_line_endings(source_file_edit.file_id);
468 let edits = source_file_edit 455 let edits = source_file_edit
469 .edit 456 .edit
470 .as_indels() 457 .into_iter()
471 .iter() 458 .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it))
472 .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it.clone()))
473 .collect(); 459 .collect();
474 Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) 460 Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
475} 461}
@@ -492,36 +478,6 @@ pub(crate) fn resource_op(
492 Ok(res) 478 Ok(res)
493} 479}
494 480
495pub(crate) fn source_change(
496 world: &WorldSnapshot,
497 source_change: SourceChange,
498) -> Result<lsp_ext::SourceChange> {
499 let cursor_position = match source_change.cursor_position {
500 None => None,
501 Some(pos) => {
502 let line_index = world.analysis().file_line_index(pos.file_id)?;
503 let edit = source_change
504 .source_file_edits
505 .iter()
506 .find(|it| it.file_id == pos.file_id)
507 .map(|it| &it.edit);
508 let line_col = match edit {
509 Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit),
510 None => line_index.line_col(pos.offset),
511 };
512 let position =
513 lsp_types::Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16));
514 Some(lsp_types::TextDocumentPositionParams {
515 text_document: text_document_identifier(world, pos.file_id)?,
516 position,
517 })
518 }
519 };
520 let label = source_change.label.clone();
521 let workspace_edit = self::snippet_workspace_edit(world, source_change)?;
522 Ok(lsp_ext::SourceChange { label, workspace_edit, cursor_position })
523}
524
525pub(crate) fn snippet_workspace_edit( 481pub(crate) fn snippet_workspace_edit(
526 world: &WorldSnapshot, 482 world: &WorldSnapshot,
527 source_change: SourceChange, 483 source_change: SourceChange,
@@ -639,25 +595,12 @@ fn main() <fold>{
639} 595}
640 596
641pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { 597pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> {
642 let res = if assist.source_change.cursor_position.is_none() { 598 let res = lsp_ext::CodeAction {
643 lsp_ext::CodeAction { 599 title: assist.label,
644 title: assist.label, 600 group: if world.config.client_caps.code_action_group { assist.group_label } else { None },
645 kind: Some(String::new()), 601 kind: Some(String::new()),
646 edit: Some(snippet_workspace_edit(world, assist.source_change)?), 602 edit: Some(snippet_workspace_edit(world, assist.source_change)?),
647 command: None, 603 command: None,
648 }
649 } else {
650 assert!(!assist.source_change.is_snippet);
651 let source_change = source_change(&world, assist.source_change)?;
652 let arg = serde_json::to_value(source_change)?;
653 let title = assist.label;
654 let command = lsp_types::Command {
655 title: title.clone(),
656 command: "rust-analyzer.applySourceChange".to_string(),
657 arguments: Some(vec![arg]),
658 };
659
660 lsp_ext::CodeAction { title, kind: Some(String::new()), edit: None, command: Some(command) }
661 }; 604 };
662 Ok(res) 605 Ok(res)
663} 606}
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 74676b3ee..738a9a8e3 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -474,27 +474,21 @@ fn main() {{}}
474 position: Position { line: 0, character: 5 }, 474 position: Position { line: 0, character: 5 },
475 }, 475 },
476 json!({ 476 json!({
477 "cursorPosition": { 477 "documentChanges": [
478 "position": { "character": 4, "line": 1 }, 478 {
479 "textDocument": { "uri": "file:///[..]src/m0.rs" } 479 "edits": [
480 }, 480 {
481 "label": "On enter", 481 "insertTextFormat": 2,
482 "workspaceEdit": { 482 "newText": "\n/// $0",
483 "documentChanges": [ 483 "range": {
484 { 484 "end": { "character": 5, "line": 0 },
485 "edits": [ 485 "start": { "character": 5, "line": 0 }
486 {
487 "newText": "\n/// ",
488 "range": {
489 "end": { "character": 5, "line": 0 },
490 "start": { "character": 5, "line": 0 }
491 }
492 } 486 }
493 ], 487 }
494 "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null } 488 ],
495 } 489 "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null }
496 ] 490 }
497 } 491 ]
498 }), 492 }),
499 ); 493 );
500 let elapsed = start.elapsed(); 494 let elapsed = start.elapsed();
@@ -526,27 +520,21 @@ version = \"0.0.0\"
526 position: Position { line: 0, character: 8 }, 520 position: Position { line: 0, character: 8 },
527 }, 521 },
528 json!({ 522 json!({
529 "cursorPosition": { 523 "documentChanges": [
530 "position": { "line": 1, "character": 4 }, 524 {
531 "textDocument": { "uri": "file:///[..]src/main.rs" } 525 "edits": [
532 }, 526 {
533 "label": "On enter", 527 "insertTextFormat": 2,
534 "workspaceEdit": { 528 "newText": "\r\n/// $0",
535 "documentChanges": [ 529 "range": {
536 { 530 "end": { "line": 0, "character": 8 },
537 "edits": [ 531 "start": { "line": 0, "character": 8 }
538 {
539 "newText": "\r\n/// ",
540 "range": {
541 "end": { "line": 0, "character": 8 },
542 "start": { "line": 0, "character": 8 }
543 }
544 } 532 }
545 ], 533 }
546 "textDocument": { "uri": "file:///[..]src/main.rs", "version": null } 534 ],
547 } 535 "textDocument": { "uri": "file:///[..]src/main.rs", "version": null }
548 ] 536 }
549 } 537 ]
550 }), 538 }),
551 ); 539 );
552} 540}
@@ -786,5 +774,5 @@ pub fn foo(_input: TokenStream) -> TokenStream {
786 }); 774 });
787 775
788 let value = res.get("contents").unwrap().get("value").unwrap().to_string(); 776 let value = res.get("contents").unwrap().get("value").unwrap().to_string();
789 assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#) 777 assert_eq!(value, r#""foo::Bar\n___\n\n```rust\nfn bar()\n```""#)
790} 778}
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index d2ec6c021..158d3c599 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -5,15 +5,11 @@ It's a best effort document, when in doubt, consult the source (and send a PR wi
5We aim to upstream all non Rust-specific extensions to the protocol, but this is not a top priority. 5We aim to upstream all non Rust-specific extensions to the protocol, but this is not a top priority.
6All capabilities are enabled via `experimental` field of `ClientCapabilities`. 6All capabilities are enabled via `experimental` field of `ClientCapabilities`.
7 7
8## `SnippetTextEdit` 8## Snippet `TextEdit`
9 9
10**Capability** 10**Issue:** https://github.com/microsoft/language-server-protocol/issues/724
11 11
12```typescript 12**Client Capability:** `{ "snippetTextEdit": boolean }`
13{
14 "snippetTextEdit": boolean
15}
16```
17 13
18If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: 14If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s:
19 15
@@ -32,3 +28,145 @@ export interface TextDocumentEdit {
32 28
33When applying such code action, the editor should insert snippet, with tab stops and placeholder. 29When applying such code action, the editor should insert snippet, with tab stops and placeholder.
34At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`. 30At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`.
31
32### Example
33
34"Add `derive`" code action transforms `struct S;` into `#[derive($0)] struct S;`
35
36### Unresolved Questions
37
38* Where exactly are `SnippetTextEdit`s allowed (only in code actions at the moment)?
39* Can snippets span multiple files (so far, no)?
40
41## Join Lines
42
43**Issue:** https://github.com/microsoft/language-server-protocol/issues/992
44
45**Server Capability:** `{ "joinLines": boolean }`
46
47This request is send from client to server to handle "Join Lines" editor action.
48
49**Method:** `experimental/JoinLines`
50
51**Request:**
52
53```typescript
54interface JoinLinesParams {
55 textDocument: TextDocumentIdentifier,
56 /// Currently active selections/cursor offsets.
57 /// This is an array to support multiple cursors.
58 ranges: Range[],
59}
60```
61
62**Response:**
63
64```typescript
65TextEdit[]
66```
67
68### Example
69
70```rust
71fn main() {
72 /*cursor here*/let x = {
73 92
74 };
75}
76```
77
78`experimental/joinLines` yields (curly braces are automagiacally removed)
79
80```rust
81fn main() {
82 let x = 92;
83}
84```
85
86### Unresolved Question
87
88* What is the position of the cursor after `joinLines`?
89 Currently this is left to editor's discretion, but it might be useful to specify on the server via snippets.
90 However, it then becomes unclear how it works with multi cursor.
91
92## Structural Search Replace (SSR)
93
94**Server Capability:** `{ "ssr": boolean }`
95
96This request is send from client to server to handle structural search replace -- automated syntax tree based transformation of the source.
97
98**Method:** `experimental/ssr`
99
100**Request:**
101
102```typescript
103interface SsrParams {
104 /// Search query.
105 /// The specific syntax is specified outside of the protocol.
106 query: string,
107 /// If true, only check the syntax of the query and don't compute the actual edit.
108 parseOnly: bool,
109}
110```
111
112**Response:**
113
114```typescript
115WorkspaceEdit
116```
117
118### Example
119
120SSR with query `foo($a:expr, $b:expr) ==>> ($a).foo($b)` will transform, eg `foo(y + 5, z)` into `(y + 5).foo(z)`.
121
122### Unresolved Question
123
124* Probably needs search without replace mode
125* Needs a way to limit the scope to certain files.
126
127## `CodeAction` Groups
128
129**Issue:** https://github.com/microsoft/language-server-protocol/issues/994
130
131**Client Capability:** `{ "codeActionGroup": boolean }`
132
133If this capability is set, `CodeAction` returned from the server contain an additional field, `group`:
134
135```typescript
136interface CodeAction {
137 title: string;
138 group?: string;
139 ...
140}
141```
142
143All code-actions with the same `group` should be grouped under single (extendable) entry in lightbulb menu.
144The set of actions `[ { title: "foo" }, { group: "frobnicate", title: "bar" }, { group: "frobnicate", title: "baz" }]` should be rendered as
145
146```
147💡
148 +-------------+
149 | foo |
150 +-------------+-----+
151 | frobnicate >| bar |
152 +-------------+-----+
153 | baz |
154 +-----+
155```
156
157Alternatively, selecting `frobnicate` could present a user with an additional menu to choose between `bar` and `baz`.
158
159### Example
160
161```rust
162fn main() {
163 let x: Entry/*cursor here*/ = todo!();
164}
165```
166
167Invoking code action at this position will yield two code actions for importing `Entry` from either `collections::HashMap` or `collection::BTreeMap`, grouped under a single "import" group.
168
169### Unresolved Questions
170
171* Is a fixed two-level structure enough?
172* Should we devise a general way to encode custom interaction protocols for GUI refactorings?
diff --git a/editors/code/package.json b/editors/code/package.json
index d899f60e3..78f647baa 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -313,22 +313,22 @@
313 "rust-analyzer.inlayHints.enable": { 313 "rust-analyzer.inlayHints.enable": {
314 "type": "boolean", 314 "type": "boolean",
315 "default": true, 315 "default": true,
316 "description": "Disable all inlay hints" 316 "description": "Whether to show inlay hints"
317 }, 317 },
318 "rust-analyzer.inlayHints.typeHints": { 318 "rust-analyzer.inlayHints.typeHints": {
319 "type": "boolean", 319 "type": "boolean",
320 "default": true, 320 "default": true,
321 "description": "Whether to show inlay type hints" 321 "description": "Whether to show inlay type hints for variables."
322 }, 322 },
323 "rust-analyzer.inlayHints.chainingHints": { 323 "rust-analyzer.inlayHints.chainingHints": {
324 "type": "boolean", 324 "type": "boolean",
325 "default": true, 325 "default": true,
326 "description": "Whether to show inlay type hints for method chains" 326 "description": "Whether to show inlay type hints for method chains."
327 }, 327 },
328 "rust-analyzer.inlayHints.parameterHints": { 328 "rust-analyzer.inlayHints.parameterHints": {
329 "type": "boolean", 329 "type": "boolean",
330 "default": true, 330 "default": true,
331 "description": "Whether to show function parameter name inlay hints at the call site" 331 "description": "Whether to show function parameter name inlay hints at the call site."
332 }, 332 },
333 "rust-analyzer.inlayHints.maxLength": { 333 "rust-analyzer.inlayHints.maxLength": {
334 "type": [ 334 "type": [
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index fac1a0be3..d64f9a3f9 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -41,10 +41,12 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
41 return client.sendRequest(lc.CodeActionRequest.type, params, token).then((values) => { 41 return client.sendRequest(lc.CodeActionRequest.type, params, token).then((values) => {
42 if (values === null) return undefined; 42 if (values === null) return undefined;
43 const result: (vscode.CodeAction | vscode.Command)[] = []; 43 const result: (vscode.CodeAction | vscode.Command)[] = [];
44 const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>();
44 for (const item of values) { 45 for (const item of values) {
45 if (lc.CodeAction.is(item)) { 46 if (lc.CodeAction.is(item)) {
46 const action = client.protocol2CodeConverter.asCodeAction(item); 47 const action = client.protocol2CodeConverter.asCodeAction(item);
47 if (isSnippetEdit(item)) { 48 const group = actionGroup(item);
49 if (isSnippetEdit(item) || group) {
48 action.command = { 50 action.command = {
49 command: "rust-analyzer.applySnippetWorkspaceEdit", 51 command: "rust-analyzer.applySnippetWorkspaceEdit",
50 title: "", 52 title: "",
@@ -52,12 +54,38 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
52 }; 54 };
53 action.edit = undefined; 55 action.edit = undefined;
54 } 56 }
55 result.push(action); 57
58 if (group) {
59 let entry = groups.get(group);
60 if (!entry) {
61 entry = { index: result.length, items: [] };
62 groups.set(group, entry);
63 result.push(action);
64 }
65 entry.items.push(action);
66 } else {
67 result.push(action);
68 }
56 } else { 69 } else {
57 const command = client.protocol2CodeConverter.asCommand(item); 70 const command = client.protocol2CodeConverter.asCommand(item);
58 result.push(command); 71 result.push(command);
59 } 72 }
60 } 73 }
74 for (const [group, { index, items }] of groups) {
75 if (items.length === 1) {
76 result[index] = items[0];
77 } else {
78 const action = new vscode.CodeAction(group);
79 action.command = {
80 command: "rust-analyzer.applyActionGroup",
81 title: "",
82 arguments: [items.map((item) => {
83 return { label: item.title, edit: item.command!!.arguments!![0] };
84 })],
85 };
86 result[index] = action;
87 }
88 }
61 return result; 89 return result;
62 }, 90 },
63 (_error) => undefined 91 (_error) => undefined
@@ -81,15 +109,16 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
81 // implementations are still in the "proposed" category for 3.16. 109 // implementations are still in the "proposed" category for 3.16.
82 client.registerFeature(new CallHierarchyFeature(client)); 110 client.registerFeature(new CallHierarchyFeature(client));
83 client.registerFeature(new SemanticTokensFeature(client)); 111 client.registerFeature(new SemanticTokensFeature(client));
84 client.registerFeature(new SnippetTextEditFeature()); 112 client.registerFeature(new ExperimentalFeatures());
85 113
86 return client; 114 return client;
87} 115}
88 116
89class SnippetTextEditFeature implements lc.StaticFeature { 117class ExperimentalFeatures implements lc.StaticFeature {
90 fillClientCapabilities(capabilities: lc.ClientCapabilities): void { 118 fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
91 const caps: any = capabilities.experimental ?? {}; 119 const caps: any = capabilities.experimental ?? {};
92 caps.snippetTextEdit = true; 120 caps.snippetTextEdit = true;
121 caps.codeActionGroup = true;
93 capabilities.experimental = caps; 122 capabilities.experimental = caps;
94 } 123 }
95 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { 124 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
@@ -107,3 +136,7 @@ function isSnippetEdit(action: lc.CodeAction): boolean {
107 } 136 }
108 return false; 137 return false;
109} 138}
139
140function actionGroup(action: lc.CodeAction): string | undefined {
141 return (action as any).group;
142}
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index 0937b495c..abb53a248 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -41,48 +41,65 @@ export function applySourceChange(ctx: Ctx): Cmd {
41 }; 41 };
42} 42}
43 43
44export function selectAndApplySourceChange(ctx: Ctx): Cmd { 44export function applyActionGroup(_ctx: Ctx): Cmd {
45 return async (changes: ra.SourceChange[]) => { 45 return async (actions: { label: string; edit: vscode.WorkspaceEdit }[]) => {
46 if (changes.length === 1) { 46 const selectedAction = await vscode.window.showQuickPick(actions);
47 await sourceChange.applySourceChange(ctx, changes[0]); 47 if (!selectedAction) return;
48 } else if (changes.length > 0) { 48 await applySnippetWorkspaceEdit(selectedAction.edit);
49 const selectedChange = await vscode.window.showQuickPick(changes);
50 if (!selectedChange) return;
51 await sourceChange.applySourceChange(ctx, selectedChange);
52 }
53 }; 49 };
54} 50}
55 51
56export function applySnippetWorkspaceEdit(_ctx: Ctx): Cmd { 52export function applySnippetWorkspaceEditCommand(_ctx: Ctx): Cmd {
57 return async (edit: vscode.WorkspaceEdit) => { 53 return async (edit: vscode.WorkspaceEdit) => {
58 assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`); 54 await applySnippetWorkspaceEdit(edit);
59 const [uri, edits] = edit.entries()[0]; 55 };
56}
60 57
61 const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); 58export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
62 if (!editor) return; 59 assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`);
60 const [uri, edits] = edit.entries()[0];
63 61
64 let editWithSnippet: vscode.TextEdit | undefined = undefined; 62 const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
65 let lineDelta = 0; 63 if (!editor) return;
66 await editor.edit((builder) => { 64
67 for (const indel of edits) { 65 let selection: vscode.Selection | undefined = undefined;
68 const isSnippet = indel.newText.indexOf('$0') !== -1 || indel.newText.indexOf('${') !== -1; 66 let lineDelta = 0;
69 if (isSnippet) { 67 await editor.edit((builder) => {
70 editWithSnippet = indel; 68 for (const indel of edits) {
71 } else { 69 const parsed = parseSnippet(indel.newText);
72 if (!editWithSnippet) { 70 if (parsed) {
73 lineDelta = (indel.newText.match(/\n/g) || []).length - (indel.range.end.line - indel.range.start.line); 71 const [newText, [placeholderStart, placeholderLength]] = parsed;
74 } 72 const prefix = newText.substr(0, placeholderStart);
75 builder.replace(indel.range, indel.newText); 73 const lastNewline = prefix.lastIndexOf('\n');
76 } 74
75 const startLine = indel.range.start.line + lineDelta + countLines(prefix);
76 const startColumn = lastNewline === -1 ?
77 indel.range.start.character + placeholderStart
78 : prefix.length - lastNewline - 1;
79 const endColumn = startColumn + placeholderLength;
80 selection = new vscode.Selection(
81 new vscode.Position(startLine, startColumn),
82 new vscode.Position(startLine, endColumn),
83 );
84 builder.replace(indel.range, newText);
85 } else {
86 lineDelta = countLines(indel.newText) - (indel.range.end.line - indel.range.start.line);
87 builder.replace(indel.range, indel.newText);
77 } 88 }
78 });
79 if (editWithSnippet) {
80 const snip = editWithSnippet as vscode.TextEdit;
81 const range = snip.range.with(
82 snip.range.start.with(snip.range.start.line + lineDelta),
83 snip.range.end.with(snip.range.end.line + lineDelta),
84 );
85 await editor.insertSnippet(new vscode.SnippetString(snip.newText), range);
86 } 89 }
87 }; 90 });
91 if (selection) editor.selection = selection;
92}
93
94function parseSnippet(snip: string): [string, [number, number]] | undefined {
95 const m = snip.match(/\$(0|\{0:([^}]*)\})/);
96 if (!m) return undefined;
97 const placeholder = m[2] ?? "";
98 const range: [number, number] = [m.index!!, placeholder.length];
99 const insert = snip.replace(m[0], placeholder);
100 return [insert, range];
101}
102
103function countLines(text: string): number {
104 return (text.match(/\n/g) || []).length;
88} 105}
diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts
index de0614653..0bf1ee6e6 100644
--- a/editors/code/src/commands/join_lines.ts
+++ b/editors/code/src/commands/join_lines.ts
@@ -1,7 +1,7 @@
1import * as ra from '../rust-analyzer-api'; 1import * as ra from '../rust-analyzer-api';
2import * as lc from 'vscode-languageclient';
2 3
3import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd } from '../ctx';
4import { applySourceChange } from '../source_change';
5 5
6export function joinLines(ctx: Ctx): Cmd { 6export function joinLines(ctx: Ctx): Cmd {
7 return async () => { 7 return async () => {
@@ -9,10 +9,14 @@ export function joinLines(ctx: Ctx): Cmd {
9 const client = ctx.client; 9 const client = ctx.client;
10 if (!editor || !client) return; 10 if (!editor || !client) return;
11 11
12 const change = await client.sendRequest(ra.joinLines, { 12 const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, {
13 range: client.code2ProtocolConverter.asRange(editor.selection), 13 ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)),
14 textDocument: { uri: editor.document.uri.toString() }, 14 textDocument: { uri: editor.document.uri.toString() },
15 }); 15 });
16 await applySourceChange(ctx, change); 16 editor.edit((builder) => {
17 client.protocol2CodeConverter.asTextEdits(items).forEach((edit) => {
18 builder.replace(edit.range, edit.newText);
19 });
20 });
17 }; 21 };
18} 22}
diff --git a/editors/code/src/commands/on_enter.ts b/editors/code/src/commands/on_enter.ts
index 285849db7..a7871c31e 100644
--- a/editors/code/src/commands/on_enter.ts
+++ b/editors/code/src/commands/on_enter.ts
@@ -1,8 +1,8 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as ra from '../rust-analyzer-api'; 2import * as ra from '../rust-analyzer-api';
3 3
4import { applySourceChange } from '../source_change';
5import { Cmd, Ctx } from '../ctx'; 4import { Cmd, Ctx } from '../ctx';
5import { applySnippetWorkspaceEdit } from '.';
6 6
7async function handleKeypress(ctx: Ctx) { 7async function handleKeypress(ctx: Ctx) {
8 const editor = ctx.activeRustEditor; 8 const editor = ctx.activeRustEditor;
@@ -21,7 +21,8 @@ async function handleKeypress(ctx: Ctx) {
21 }); 21 });
22 if (!change) return false; 22 if (!change) return false;
23 23
24 await applySourceChange(ctx, change); 24 const workspaceEdit = client.protocol2CodeConverter.asWorkspaceEdit(change);
25 await applySnippetWorkspaceEdit(workspaceEdit);
25 return true; 26 return true;
26} 27}
27 28
diff --git a/editors/code/src/commands/ssr.ts b/editors/code/src/commands/ssr.ts
index 4ef8cdf04..5d40a64d2 100644
--- a/editors/code/src/commands/ssr.ts
+++ b/editors/code/src/commands/ssr.ts
@@ -2,7 +2,6 @@ import * as vscode from 'vscode';
2import * as ra from "../rust-analyzer-api"; 2import * as ra from "../rust-analyzer-api";
3 3
4import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd } from '../ctx';
5import { applySourceChange } from '../source_change';
6 5
7export function ssr(ctx: Ctx): Cmd { 6export function ssr(ctx: Ctx): Cmd {
8 return async () => { 7 return async () => {
@@ -22,11 +21,10 @@ export function ssr(ctx: Ctx): Cmd {
22 } 21 }
23 }; 22 };
24 const request = await vscode.window.showInputBox(options); 23 const request = await vscode.window.showInputBox(options);
25
26 if (!request) return; 24 if (!request) return;
27 25
28 const change = await client.sendRequest(ra.ssr, { query: request, parseOnly: false }); 26 const edit = await client.sendRequest(ra.ssr, { query: request, parseOnly: false });
29 27
30 await applySourceChange(ctx, change); 28 await vscode.workspace.applyEdit(client.protocol2CodeConverter.asWorkspaceEdit(edit));
31 }; 29 };
32} 30}
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index ad3c1a192..3405634f3 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -92,8 +92,8 @@ export async function activate(context: vscode.ExtensionContext) {
92 ctx.registerCommand('debugSingle', commands.debugSingle); 92 ctx.registerCommand('debugSingle', commands.debugSingle);
93 ctx.registerCommand('showReferences', commands.showReferences); 93 ctx.registerCommand('showReferences', commands.showReferences);
94 ctx.registerCommand('applySourceChange', commands.applySourceChange); 94 ctx.registerCommand('applySourceChange', commands.applySourceChange);
95 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEdit); 95 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand);
96 ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); 96 ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
97 97
98 ctx.pushCleanup(activateTaskProvider(workspaceFolder)); 98 ctx.pushCleanup(activateTaskProvider(workspaceFolder));
99 99
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts
index 400ac3714..73f36432f 100644
--- a/editors/code/src/rust-analyzer-api.ts
+++ b/editors/code/src/rust-analyzer-api.ts
@@ -64,12 +64,12 @@ export const parentModule = request<lc.TextDocumentPositionParams, Vec<lc.Locati
64 64
65export interface JoinLinesParams { 65export interface JoinLinesParams {
66 textDocument: lc.TextDocumentIdentifier; 66 textDocument: lc.TextDocumentIdentifier;
67 range: lc.Range; 67 ranges: lc.Range[];
68} 68}
69export const joinLines = request<JoinLinesParams, SourceChange>("joinLines"); 69export const joinLines = new lc.RequestType<JoinLinesParams, lc.TextEdit[], unknown>('experimental/joinLines');
70 70
71 71
72export const onEnter = request<lc.TextDocumentPositionParams, Option<SourceChange>>("onEnter"); 72export const onEnter = request<lc.TextDocumentPositionParams, Option<lc.WorkspaceEdit>>("onEnter");
73 73
74export interface RunnablesParams { 74export interface RunnablesParams {
75 textDocument: lc.TextDocumentIdentifier; 75 textDocument: lc.TextDocumentIdentifier;
@@ -112,7 +112,7 @@ export interface SsrParams {
112 query: string; 112 query: string;
113 parseOnly: boolean; 113 parseOnly: boolean;
114} 114}
115export const ssr = request<SsrParams, SourceChange>("ssr"); 115export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, unknown>('experimental/ssr');
116 116
117 117
118export const publishDecorations = notification<PublishDecorationsParams>("publishDecorations"); 118export const publishDecorations = notification<PublishDecorationsParams>("publishDecorations");