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_hir_def/src/lang_item.rs12
-rw-r--r--crates/ra_hir_ty/Cargo.toml6
-rw-r--r--crates/ra_hir_ty/src/db.rs2
-rw-r--r--crates/ra_hir_ty/src/lib.rs2
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs78
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs868
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/interner.rs351
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/mapping.rs672
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/tls.rs69
-rw-r--r--crates/ra_ide/src/diagnostics.rs101
-rw-r--r--crates/ra_ide/src/lib.rs18
-rw-r--r--crates/ra_ide/src/references/rename.rs11
-rw-r--r--crates/ra_ide/src/typing.rs35
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs5
-rw-r--r--crates/ra_ide_db/src/source_change.rs79
-rw-r--r--crates/rust-analyzer/src/bin/main.rs17
-rw-r--r--crates/rust-analyzer/src/caps.rs61
-rw-r--r--crates/rust-analyzer/src/config.rs9
-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.rs14
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs64
-rw-r--r--crates/rust-analyzer/src/to_proto.rs10
-rw-r--r--docs/dev/lsp-extensions.md90
-rw-r--r--editors/code/src/client.ts41
-rw-r--r--editors/code/src/commands/index.ts14
-rw-r--r--editors/code/src/commands/ssr.ts6
-rw-r--r--editors/code/src/main.ts2
-rw-r--r--editors/code/src/rust-analyzer-api.ts2
32 files changed, 1580 insertions, 1117 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_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..dfc6c7dd6 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -89,7 +89,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
89 fn trait_datum(&self, krate: CrateId, trait_id: chalk::TraitId) -> Arc<chalk::TraitDatum>; 89 fn trait_datum(&self, krate: CrateId, trait_id: chalk::TraitId) -> Arc<chalk::TraitDatum>;
90 90
91 #[salsa::invoke(chalk::struct_datum_query)] 91 #[salsa::invoke(chalk::struct_datum_query)]
92 fn struct_datum(&self, krate: CrateId, struct_id: chalk::StructId) -> Arc<chalk::StructDatum>; 92 fn struct_datum(&self, krate: CrateId, struct_id: chalk::AdtId) -> Arc<chalk::StructDatum>;
93 93
94 #[salsa::invoke(crate::traits::chalk::impl_datum_query)] 94 #[salsa::invoke(crate::traits::chalk::impl_datum_query)]
95 fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc<chalk::ImplDatum>; 95 fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc<chalk::ImplDatum>;
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index c87ee06ce..e91c9be04 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -155,7 +155,7 @@ 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
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 34f4b9039..6826610cb 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -2602,3 +2602,81 @@ 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_sized() {
2648 assert_snapshot!(
2649 infer_with_mismatches(r#"
2650#[lang = "sized"]
2651trait Sized {}
2652
2653trait Test { fn test(&self) -> bool; }
2654impl<T: Sized> Test for T {}
2655
2656fn test() {
2657 1u8.test();
2658 (*"foo").test(); // not Sized
2659 (1u8, 1u8).test();
2660 (1u8, *"foo").test(); // not Sized
2661}
2662"#, true),
2663 @r###"
2664 57..61 'self': &Self
2665 114..229 '{ ...ized }': ()
2666 120..123 '1u8': u8
2667 120..130 '1u8.test()': bool
2668 136..151 '(*"foo").test()': {unknown}
2669 137..143 '*"foo"': str
2670 138..143 '"foo"': &str
2671 170..180 '(1u8, 1u8)': (u8, u8)
2672 170..187 '(1u8, ...test()': bool
2673 171..174 '1u8': u8
2674 176..179 '1u8': u8
2675 193..206 '(1u8, *"foo")': (u8, str)
2676 193..213 '(1u8, ...test()': {unknown}
2677 194..197 '1u8': u8
2678 199..205 '*"foo"': str
2679 200..205 '"foo"': &str
2680 "###
2681 );
2682}
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index 5870618a0..e2f2a9ccb 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 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 23
228 fn intern_program_clauses<E>( 24pub(super) mod tls;
229 &self, 25mod interner;
230 data: impl IntoIterator<Item = Result<chalk_ir::ProgramClause<Self>, E>>, 26mod mapping;
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
263 fn parameter_kinds_data<'a>(
264 &self,
265 parameter_kinds: &'a Self::InternedParameterKinds,
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,25 @@ 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 // We don't yet provide any FnDefs to Chalk
60 unimplemented!()
61 }
62
828 fn impls_for_trait( 63 fn impls_for_trait(
829 &self, 64 &self,
830 trait_id: TraitId, 65 trait_id: TraitId,
831 parameters: &[Parameter<Interner>], 66 parameters: &[GenericArg<Interner>],
832 ) -> Vec<ImplId> { 67 ) -> Vec<ImplId> {
833 debug!("impls_for_trait {:?}", trait_id); 68 debug!("impls_for_trait {:?}", trait_id);
834 let trait_: hir_def::TraitId = from_chalk(self.db, trait_id); 69 let trait_: hir_def::TraitId = from_chalk(self.db, trait_id);
@@ -859,7 +94,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
859 debug!("impls_for_trait returned {} impls", result.len()); 94 debug!("impls_for_trait returned {} impls", result.len());
860 result 95 result
861 } 96 }
862 fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: StructId) -> bool { 97 fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: AdtId) -> bool {
863 debug!("impl_provided_for {:?}, {:?}", auto_trait_id, struct_id); 98 debug!("impl_provided_for {:?}, {:?}", auto_trait_id, struct_id);
864 false // FIXME 99 false // FIXME
865 } 100 }
@@ -878,10 +113,15 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
878 } 113 }
879 fn well_known_trait_id( 114 fn well_known_trait_id(
880 &self, 115 &self,
881 _well_known_trait: chalk_rust_ir::WellKnownTrait, 116 well_known_trait: chalk_rust_ir::WellKnownTrait,
882 ) -> Option<chalk_ir::TraitId<Interner>> { 117 ) -> Option<chalk_ir::TraitId<Interner>> {
883 // FIXME tell Chalk about well-known traits (here and in trait_datum) 118 let lang_attr = lang_attr_from_well_known_trait(well_known_trait);
884 None 119 let lang_items = self.db.crate_lang_items(self.krate);
120 let trait_ = match lang_items.target(lang_attr) {
121 Some(LangItemTarget::TraitId(trait_)) => trait_,
122 _ => return None,
123 };
124 Some(trait_.to_chalk(self.db))
885 } 125 }
886 126
887 fn program_clauses_for_env( 127 fn program_clauses_for_env(
@@ -983,7 +223,8 @@ pub(crate) fn trait_datum_query(
983 let associated_ty_ids = 223 let associated_ty_ids =
984 trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect(); 224 trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect();
985 let trait_datum_bound = chalk_rust_ir::TraitDatumBound { where_clauses }; 225 let trait_datum_bound = chalk_rust_ir::TraitDatumBound { where_clauses };
986 let well_known = None; // FIXME set this (depending on lang items) 226 let well_known =
227 lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name));
987 let trait_datum = TraitDatum { 228 let trait_datum = TraitDatum {
988 id: trait_id, 229 id: trait_id,
989 binders: make_binders(trait_datum_bound, bound_vars.len()), 230 binders: make_binders(trait_datum_bound, bound_vars.len()),
@@ -994,13 +235,32 @@ pub(crate) fn trait_datum_query(
994 Arc::new(trait_datum) 235 Arc::new(trait_datum)
995} 236}
996 237
238fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> {
239 Some(match name {
240 "sized" => WellKnownTrait::SizedTrait,
241 "copy" => WellKnownTrait::CopyTrait,
242 "clone" => WellKnownTrait::CloneTrait,
243 "drop" => WellKnownTrait::DropTrait,
244 _ => return None,
245 })
246}
247
248fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str {
249 match attr {
250 WellKnownTrait::SizedTrait => "sized",
251 WellKnownTrait::CopyTrait => "copy",
252 WellKnownTrait::CloneTrait => "clone",
253 WellKnownTrait::DropTrait => "drop",
254 }
255}
256
997pub(crate) fn struct_datum_query( 257pub(crate) fn struct_datum_query(
998 db: &dyn HirDatabase, 258 db: &dyn HirDatabase,
999 krate: CrateId, 259 krate: CrateId,
1000 struct_id: StructId, 260 struct_id: AdtId,
1001) -> Arc<StructDatum> { 261) -> Arc<StructDatum> {
1002 debug!("struct_datum {:?}", struct_id); 262 debug!("struct_datum {:?}", struct_id);
1003 let type_ctor: TypeCtor = from_chalk(db, TypeName::Struct(struct_id)); 263 let type_ctor: TypeCtor = from_chalk(db, TypeName::Adt(struct_id));
1004 debug!("struct {:?} = {:?}", struct_id, type_ctor); 264 debug!("struct {:?} = {:?}", struct_id, type_ctor);
1005 let num_params = type_ctor.num_ty_params(db); 265 let num_params = type_ctor.num_ty_params(db);
1006 let upstream = type_ctor.krate(db) != Some(krate); 266 let upstream = type_ctor.krate(db) != Some(krate);
@@ -1012,12 +272,12 @@ pub(crate) fn struct_datum_query(
1012 convert_where_clauses(db, generic_def, &bound_vars) 272 convert_where_clauses(db, generic_def, &bound_vars)
1013 }) 273 })
1014 .unwrap_or_else(Vec::new); 274 .unwrap_or_else(Vec::new);
1015 let flags = chalk_rust_ir::StructFlags { 275 let flags = chalk_rust_ir::AdtFlags {
1016 upstream, 276 upstream,
1017 // FIXME set fundamental flag correctly 277 // FIXME set fundamental flag correctly
1018 fundamental: false, 278 fundamental: false,
1019 }; 279 };
1020 let struct_datum_bound = chalk_rust_ir::StructDatumBound { 280 let struct_datum_bound = chalk_rust_ir::AdtDatumBound {
1021 fields: Vec::new(), // FIXME add fields (only relevant for auto traits) 281 fields: Vec::new(), // FIXME add fields (only relevant for auto traits)
1022 where_clauses, 282 where_clauses,
1023 }; 283 };
@@ -1145,15 +405,15 @@ fn type_alias_associated_ty_value(
1145 Arc::new(value) 405 Arc::new(value)
1146} 406}
1147 407
1148impl From<StructId> for crate::TypeCtorId { 408impl From<AdtId> for crate::TypeCtorId {
1149 fn from(struct_id: StructId) -> Self { 409 fn from(struct_id: AdtId) -> Self {
1150 InternKey::from_intern_id(struct_id.0) 410 struct_id.0
1151 } 411 }
1152} 412}
1153 413
1154impl From<crate::TypeCtorId> for StructId { 414impl From<crate::TypeCtorId> for AdtId {
1155 fn from(type_ctor_id: crate::TypeCtorId) -> Self { 415 fn from(type_ctor_id: crate::TypeCtorId) -> Self {
1156 chalk_ir::StructId(type_ctor_id.as_intern_id()) 416 chalk_ir::AdtId(type_ctor_id)
1157 } 417 }
1158} 418}
1159 419
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..060372819
--- /dev/null
+++ b/crates/ra_hir_ty/src/traits/chalk/interner.rs
@@ -0,0 +1,351 @@
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>;
23
24impl chalk_ir::interner::Interner for Interner {
25 type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc?
26 type InternedLifetime = chalk_ir::LifetimeData<Self>;
27 type InternedConst = Arc<chalk_ir::ConstData<Self>>;
28 type InternedConcreteConst = ();
29 type InternedGenericArg = chalk_ir::GenericArgData<Self>;
30 type InternedGoal = Arc<GoalData<Self>>;
31 type InternedGoals = Vec<Goal<Self>>;
32 type InternedSubstitution = Vec<GenericArg<Self>>;
33 type InternedProgramClause = chalk_ir::ProgramClauseData<Self>;
34 type InternedProgramClauses = Arc<[chalk_ir::ProgramClause<Self>]>;
35 type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>;
36 type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>;
37 type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>;
38 type DefId = InternId;
39 type InternedAdtId = crate::TypeCtorId;
40 type Identifier = TypeAliasId;
41
42 fn debug_adt_id(type_kind_id: AdtId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
43 tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt)))
44 }
45
46 fn debug_trait_id(type_kind_id: TraitId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
47 tls::with_current_program(|prog| Some(prog?.debug_trait_id(type_kind_id, fmt)))
48 }
49
50 fn debug_assoc_type_id(id: AssocTypeId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
51 tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt)))
52 }
53
54 fn debug_alias(
55 alias: &chalk_ir::AliasTy<Interner>,
56 fmt: &mut fmt::Formatter<'_>,
57 ) -> Option<fmt::Result> {
58 tls::with_current_program(|prog| Some(prog?.debug_alias(alias, fmt)))
59 }
60
61 fn debug_projection_ty(
62 proj: &chalk_ir::ProjectionTy<Interner>,
63 fmt: &mut fmt::Formatter<'_>,
64 ) -> Option<fmt::Result> {
65 tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt)))
66 }
67
68 fn debug_opaque_ty(
69 opaque_ty: &chalk_ir::OpaqueTy<Interner>,
70 fmt: &mut fmt::Formatter<'_>,
71 ) -> Option<fmt::Result> {
72 tls::with_current_program(|prog| Some(prog?.debug_opaque_ty(opaque_ty, fmt)))
73 }
74
75 fn debug_opaque_ty_id(
76 opaque_ty_id: chalk_ir::OpaqueTyId<Self>,
77 fmt: &mut fmt::Formatter<'_>,
78 ) -> Option<fmt::Result> {
79 tls::with_current_program(|prog| Some(prog?.debug_opaque_ty_id(opaque_ty_id, fmt)))
80 }
81
82 fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
83 tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt)))
84 }
85
86 fn debug_lifetime(
87 lifetime: &chalk_ir::Lifetime<Interner>,
88 fmt: &mut fmt::Formatter<'_>,
89 ) -> Option<fmt::Result> {
90 tls::with_current_program(|prog| Some(prog?.debug_lifetime(lifetime, fmt)))
91 }
92
93 fn debug_generic_arg(
94 parameter: &GenericArg<Interner>,
95 fmt: &mut fmt::Formatter<'_>,
96 ) -> Option<fmt::Result> {
97 tls::with_current_program(|prog| Some(prog?.debug_generic_arg(parameter, fmt)))
98 }
99
100 fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
101 tls::with_current_program(|prog| Some(prog?.debug_goal(goal, fmt)))
102 }
103
104 fn debug_goals(
105 goals: &chalk_ir::Goals<Interner>,
106 fmt: &mut fmt::Formatter<'_>,
107 ) -> Option<fmt::Result> {
108 tls::with_current_program(|prog| Some(prog?.debug_goals(goals, fmt)))
109 }
110
111 fn debug_program_clause_implication(
112 pci: &chalk_ir::ProgramClauseImplication<Interner>,
113 fmt: &mut fmt::Formatter<'_>,
114 ) -> Option<fmt::Result> {
115 tls::with_current_program(|prog| Some(prog?.debug_program_clause_implication(pci, fmt)))
116 }
117
118 fn debug_application_ty(
119 application_ty: &chalk_ir::ApplicationTy<Interner>,
120 fmt: &mut fmt::Formatter<'_>,
121 ) -> Option<fmt::Result> {
122 tls::with_current_program(|prog| Some(prog?.debug_application_ty(application_ty, fmt)))
123 }
124
125 fn debug_substitution(
126 substitution: &chalk_ir::Substitution<Interner>,
127 fmt: &mut fmt::Formatter<'_>,
128 ) -> Option<fmt::Result> {
129 tls::with_current_program(|prog| Some(prog?.debug_substitution(substitution, fmt)))
130 }
131
132 fn debug_separator_trait_ref(
133 separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>,
134 fmt: &mut fmt::Formatter<'_>,
135 ) -> Option<fmt::Result> {
136 tls::with_current_program(|prog| {
137 Some(prog?.debug_separator_trait_ref(separator_trait_ref, fmt))
138 })
139 }
140
141 fn debug_fn_def_id(
142 fn_def_id: chalk_ir::FnDefId<Self>,
143 fmt: &mut fmt::Formatter<'_>,
144 ) -> Option<fmt::Result> {
145 tls::with_current_program(|prog| Some(prog?.debug_fn_def_id(fn_def_id, fmt)))
146 }
147 fn debug_const(
148 constant: &chalk_ir::Const<Self>,
149 fmt: &mut fmt::Formatter<'_>,
150 ) -> Option<fmt::Result> {
151 tls::with_current_program(|prog| Some(prog?.debug_const(constant, fmt)))
152 }
153 fn debug_variable_kinds(
154 variable_kinds: &chalk_ir::VariableKinds<Self>,
155 fmt: &mut fmt::Formatter<'_>,
156 ) -> Option<fmt::Result> {
157 tls::with_current_program(|prog| Some(prog?.debug_variable_kinds(variable_kinds, fmt)))
158 }
159 fn debug_variable_kinds_with_angles(
160 variable_kinds: &chalk_ir::VariableKinds<Self>,
161 fmt: &mut fmt::Formatter<'_>,
162 ) -> Option<fmt::Result> {
163 tls::with_current_program(|prog| {
164 Some(prog?.debug_variable_kinds_with_angles(variable_kinds, fmt))
165 })
166 }
167 fn debug_canonical_var_kinds(
168 canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Self>,
169 fmt: &mut fmt::Formatter<'_>,
170 ) -> Option<fmt::Result> {
171 tls::with_current_program(|prog| {
172 Some(prog?.debug_canonical_var_kinds(canonical_var_kinds, fmt))
173 })
174 }
175 fn debug_program_clause(
176 clause: &chalk_ir::ProgramClause<Self>,
177 fmt: &mut fmt::Formatter<'_>,
178 ) -> Option<fmt::Result> {
179 tls::with_current_program(|prog| Some(prog?.debug_program_clause(clause, fmt)))
180 }
181 fn debug_program_clauses(
182 clauses: &chalk_ir::ProgramClauses<Self>,
183 fmt: &mut fmt::Formatter<'_>,
184 ) -> Option<fmt::Result> {
185 tls::with_current_program(|prog| Some(prog?.debug_program_clauses(clauses, fmt)))
186 }
187 fn debug_quantified_where_clauses(
188 clauses: &chalk_ir::QuantifiedWhereClauses<Self>,
189 fmt: &mut fmt::Formatter<'_>,
190 ) -> Option<fmt::Result> {
191 tls::with_current_program(|prog| Some(prog?.debug_quantified_where_clauses(clauses, fmt)))
192 }
193
194 fn intern_ty(&self, ty: chalk_ir::TyData<Self>) -> Box<chalk_ir::TyData<Self>> {
195 Box::new(ty)
196 }
197
198 fn ty_data<'a>(&self, ty: &'a Box<chalk_ir::TyData<Self>>) -> &'a chalk_ir::TyData<Self> {
199 ty
200 }
201
202 fn intern_lifetime(
203 &self,
204 lifetime: chalk_ir::LifetimeData<Self>,
205 ) -> chalk_ir::LifetimeData<Self> {
206 lifetime
207 }
208
209 fn lifetime_data<'a>(
210 &self,
211 lifetime: &'a chalk_ir::LifetimeData<Self>,
212 ) -> &'a chalk_ir::LifetimeData<Self> {
213 lifetime
214 }
215
216 fn intern_const(&self, constant: chalk_ir::ConstData<Self>) -> Arc<chalk_ir::ConstData<Self>> {
217 Arc::new(constant)
218 }
219
220 fn const_data<'a>(
221 &self,
222 constant: &'a Arc<chalk_ir::ConstData<Self>>,
223 ) -> &'a chalk_ir::ConstData<Self> {
224 constant
225 }
226
227 fn const_eq(&self, _ty: &Box<chalk_ir::TyData<Self>>, _c1: &(), _c2: &()) -> bool {
228 true
229 }
230
231 fn intern_generic_arg(
232 &self,
233 parameter: chalk_ir::GenericArgData<Self>,
234 ) -> chalk_ir::GenericArgData<Self> {
235 parameter
236 }
237
238 fn generic_arg_data<'a>(
239 &self,
240 parameter: &'a chalk_ir::GenericArgData<Self>,
241 ) -> &'a chalk_ir::GenericArgData<Self> {
242 parameter
243 }
244
245 fn intern_goal(&self, goal: GoalData<Self>) -> Arc<GoalData<Self>> {
246 Arc::new(goal)
247 }
248
249 fn intern_goals<E>(
250 &self,
251 data: impl IntoIterator<Item = Result<Goal<Self>, E>>,
252 ) -> Result<Self::InternedGoals, E> {
253 data.into_iter().collect()
254 }
255
256 fn goal_data<'a>(&self, goal: &'a Arc<GoalData<Self>>) -> &'a GoalData<Self> {
257 goal
258 }
259
260 fn goals_data<'a>(&self, goals: &'a Vec<Goal<Interner>>) -> &'a [Goal<Interner>] {
261 goals
262 }
263
264 fn intern_substitution<E>(
265 &self,
266 data: impl IntoIterator<Item = Result<GenericArg<Self>, E>>,
267 ) -> Result<Vec<GenericArg<Self>>, E> {
268 data.into_iter().collect()
269 }
270
271 fn substitution_data<'a>(
272 &self,
273 substitution: &'a Vec<GenericArg<Self>>,
274 ) -> &'a [GenericArg<Self>] {
275 substitution
276 }
277
278 fn intern_program_clause(
279 &self,
280 data: chalk_ir::ProgramClauseData<Self>,
281 ) -> chalk_ir::ProgramClauseData<Self> {
282 data
283 }
284
285 fn program_clause_data<'a>(
286 &self,
287 clause: &'a chalk_ir::ProgramClauseData<Self>,
288 ) -> &'a chalk_ir::ProgramClauseData<Self> {
289 clause
290 }
291
292 fn intern_program_clauses<E>(
293 &self,
294 data: impl IntoIterator<Item = Result<chalk_ir::ProgramClause<Self>, E>>,
295 ) -> Result<Arc<[chalk_ir::ProgramClause<Self>]>, E> {
296 data.into_iter().collect()
297 }
298
299 fn program_clauses_data<'a>(
300 &self,
301 clauses: &'a Arc<[chalk_ir::ProgramClause<Self>]>,
302 ) -> &'a [chalk_ir::ProgramClause<Self>] {
303 &clauses
304 }
305
306 fn intern_quantified_where_clauses<E>(
307 &self,
308 data: impl IntoIterator<Item = Result<chalk_ir::QuantifiedWhereClause<Self>, E>>,
309 ) -> Result<Self::InternedQuantifiedWhereClauses, E> {
310 data.into_iter().collect()
311 }
312
313 fn quantified_where_clauses_data<'a>(
314 &self,
315 clauses: &'a Self::InternedQuantifiedWhereClauses,
316 ) -> &'a [chalk_ir::QuantifiedWhereClause<Self>] {
317 clauses
318 }
319
320 fn intern_generic_arg_kinds<E>(
321 &self,
322 data: impl IntoIterator<Item = Result<chalk_ir::VariableKind<Self>, E>>,
323 ) -> Result<Self::InternedVariableKinds, E> {
324 data.into_iter().collect()
325 }
326
327 fn variable_kinds_data<'a>(
328 &self,
329 parameter_kinds: &'a Self::InternedVariableKinds,
330 ) -> &'a [chalk_ir::VariableKind<Self>] {
331 &parameter_kinds
332 }
333
334 fn intern_canonical_var_kinds<E>(
335 &self,
336 data: impl IntoIterator<Item = Result<chalk_ir::CanonicalVarKind<Self>, E>>,
337 ) -> Result<Self::InternedCanonicalVarKinds, E> {
338 data.into_iter().collect()
339 }
340
341 fn canonical_var_kinds_data<'a>(
342 &self,
343 canonical_var_kinds: &'a Self::InternedCanonicalVarKinds,
344 ) -> &'a [chalk_ir::CanonicalVarKind<Self>] {
345 &canonical_var_kinds
346 }
347}
348
349impl chalk_ir::interner::HasInterner for Interner {
350 type Interner = Self;
351}
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..a83d82fd8
--- /dev/null
+++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
@@ -0,0 +1,672 @@
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, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy, Substs,
19 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) => {
30 if let TypeCtor::Ref(m) = apply_ty.ctor {
31 return ref_to_chalk(db, m, apply_ty.parameters);
32 }
33 let name = apply_ty.ctor.to_chalk(db);
34 let substitution = apply_ty.parameters.to_chalk(db);
35 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
36 }
37 Ty::Projection(proj_ty) => {
38 let associated_ty_id = proj_ty.associated_ty.to_chalk(db);
39 let substitution = proj_ty.parameters.to_chalk(db);
40 chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
41 associated_ty_id,
42 substitution,
43 })
44 .cast(&Interner)
45 .intern(&Interner)
46 }
47 Ty::Placeholder(id) => {
48 let interned_id = db.intern_type_param_id(id);
49 PlaceholderIndex {
50 ui: UniverseIndex::ROOT,
51 idx: interned_id.as_intern_id().as_usize(),
52 }
53 .to_ty::<Interner>(&Interner)
54 }
55 Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx).intern(&Interner),
56 Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"),
57 Ty::Dyn(predicates) => {
58 let where_clauses = chalk_ir::QuantifiedWhereClauses::from(
59 &Interner,
60 predicates.iter().filter(|p| !p.is_error()).cloned().map(|p| p.to_chalk(db)),
61 );
62 let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) };
63 chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner)
64 }
65 Ty::Opaque(_) | Ty::Unknown => {
66 let substitution = chalk_ir::Substitution::empty(&Interner);
67 let name = TypeName::Error;
68 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
69 }
70 }
71 }
72 fn from_chalk(db: &dyn HirDatabase, chalk: chalk_ir::Ty<Interner>) -> Self {
73 match chalk.data(&Interner).clone() {
74 chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name {
75 TypeName::Error => Ty::Unknown,
76 TypeName::Ref(m) => ref_from_chalk(db, m, apply_ty.substitution),
77 _ => {
78 let ctor = from_chalk(db, apply_ty.name);
79 let parameters = from_chalk(db, apply_ty.substitution);
80 Ty::Apply(ApplicationTy { ctor, parameters })
81 }
82 },
83 chalk_ir::TyData::Placeholder(idx) => {
84 assert_eq!(idx.ui, UniverseIndex::ROOT);
85 let interned_id = crate::db::GlobalTypeParamId::from_intern_id(
86 crate::salsa::InternId::from(idx.idx),
87 );
88 Ty::Placeholder(db.lookup_intern_type_param_id(interned_id))
89 }
90 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => {
91 let associated_ty = from_chalk(db, proj.associated_ty_id);
92 let parameters = from_chalk(db, proj.substitution);
93 Ty::Projection(ProjectionTy { associated_ty, parameters })
94 }
95 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(),
96 chalk_ir::TyData::Function(_) => unimplemented!(),
97 chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx),
98 chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown,
99 chalk_ir::TyData::Dyn(where_clauses) => {
100 assert_eq!(where_clauses.bounds.binders.len(&Interner), 1);
101 let predicates = where_clauses
102 .bounds
103 .skip_binders()
104 .iter(&Interner)
105 .map(|c| from_chalk(db, c.clone()))
106 .collect();
107 Ty::Dyn(predicates)
108 }
109 }
110 }
111}
112
113const LIFETIME_PLACEHOLDER: PlaceholderIndex =
114 PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::MAX };
115
116/// We currently don't model lifetimes, but Chalk does. So, we have to insert a
117/// fake lifetime here, because Chalks built-in logic may expect it to be there.
118fn ref_to_chalk(
119 db: &dyn HirDatabase,
120 mutability: Mutability,
121 subst: Substs,
122) -> chalk_ir::Ty<Interner> {
123 let arg = subst[0].clone().to_chalk(db);
124 let lifetime = LIFETIME_PLACEHOLDER.to_lifetime(&Interner);
125 chalk_ir::ApplicationTy {
126 name: TypeName::Ref(mutability.to_chalk(db)),
127 substitution: chalk_ir::Substitution::from(
128 &Interner,
129 vec![lifetime.cast(&Interner), arg.cast(&Interner)],
130 ),
131 }
132 .intern(&Interner)
133}
134
135/// Here we remove the lifetime from the type we got from Chalk.
136fn ref_from_chalk(
137 db: &dyn HirDatabase,
138 mutability: chalk_ir::Mutability,
139 subst: chalk_ir::Substitution<Interner>,
140) -> Ty {
141 let tys = subst
142 .iter(&Interner)
143 .filter_map(|p| Some(from_chalk(db, p.ty(&Interner)?.clone())))
144 .collect();
145 Ty::apply(TypeCtor::Ref(from_chalk(db, mutability)), Substs(tys))
146}
147
148impl ToChalk for Substs {
149 type Chalk = chalk_ir::Substitution<Interner>;
150
151 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Substitution<Interner> {
152 chalk_ir::Substitution::from(&Interner, self.iter().map(|ty| ty.clone().to_chalk(db)))
153 }
154
155 fn from_chalk(db: &dyn HirDatabase, parameters: chalk_ir::Substitution<Interner>) -> Substs {
156 let tys = parameters
157 .iter(&Interner)
158 .map(|p| match p.ty(&Interner) {
159 Some(ty) => from_chalk(db, ty.clone()),
160 None => unimplemented!(),
161 })
162 .collect();
163 Substs(tys)
164 }
165}
166
167impl ToChalk for TraitRef {
168 type Chalk = chalk_ir::TraitRef<Interner>;
169
170 fn to_chalk(self: TraitRef, db: &dyn HirDatabase) -> chalk_ir::TraitRef<Interner> {
171 let trait_id = self.trait_.to_chalk(db);
172 let substitution = self.substs.to_chalk(db);
173 chalk_ir::TraitRef { trait_id, substitution }
174 }
175
176 fn from_chalk(db: &dyn HirDatabase, trait_ref: chalk_ir::TraitRef<Interner>) -> Self {
177 let trait_ = from_chalk(db, trait_ref.trait_id);
178 let substs = from_chalk(db, trait_ref.substitution);
179 TraitRef { trait_, substs }
180 }
181}
182
183impl ToChalk for hir_def::TraitId {
184 type Chalk = TraitId;
185
186 fn to_chalk(self, _db: &dyn HirDatabase) -> TraitId {
187 chalk_ir::TraitId(self.as_intern_id())
188 }
189
190 fn from_chalk(_db: &dyn HirDatabase, trait_id: TraitId) -> hir_def::TraitId {
191 InternKey::from_intern_id(trait_id.0)
192 }
193}
194
195impl ToChalk for TypeCtor {
196 type Chalk = TypeName<Interner>;
197
198 fn to_chalk(self, db: &dyn HirDatabase) -> TypeName<Interner> {
199 match self {
200 TypeCtor::AssociatedType(type_alias) => {
201 let type_id = type_alias.to_chalk(db);
202 TypeName::AssociatedType(type_id)
203 }
204
205 TypeCtor::Bool => TypeName::Scalar(Scalar::Bool),
206 TypeCtor::Char => TypeName::Scalar(Scalar::Char),
207 TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)),
208 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 })) => {
209 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32))
210 }
211 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 })) => {
212 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64))
213 }
214
215 TypeCtor::Tuple { cardinality } => TypeName::Tuple(cardinality.into()),
216 TypeCtor::RawPtr(mutability) => TypeName::Raw(mutability.to_chalk(db)),
217 TypeCtor::Slice => TypeName::Slice,
218 TypeCtor::Ref(mutability) => TypeName::Ref(mutability.to_chalk(db)),
219 TypeCtor::Str => TypeName::Str,
220 TypeCtor::Int(Uncertain::Unknown)
221 | TypeCtor::Float(Uncertain::Unknown)
222 | TypeCtor::Adt(_)
223 | TypeCtor::Array
224 | TypeCtor::FnDef(_)
225 | TypeCtor::FnPtr { .. }
226 | TypeCtor::Never
227 | TypeCtor::Closure { .. } => {
228 // other TypeCtors get interned and turned into a chalk StructId
229 let struct_id = db.intern_type_ctor(self).into();
230 TypeName::Adt(struct_id)
231 }
232 }
233 }
234
235 fn from_chalk(db: &dyn HirDatabase, type_name: TypeName<Interner>) -> TypeCtor {
236 match type_name {
237 TypeName::Adt(struct_id) => db.lookup_intern_type_ctor(struct_id.into()),
238 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
239 TypeName::OpaqueType(_) => unreachable!(),
240
241 TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool,
242 TypeName::Scalar(Scalar::Char) => TypeCtor::Char,
243 TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(Uncertain::Known(IntTy {
244 signedness: Signedness::Signed,
245 bitness: bitness_from_chalk_int(int_ty),
246 })),
247 TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(Uncertain::Known(IntTy {
248 signedness: Signedness::Unsigned,
249 bitness: bitness_from_chalk_uint(uint_ty),
250 })),
251 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) => {
252 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 }))
253 }
254 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) => {
255 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 }))
256 }
257 TypeName::Tuple(cardinality) => TypeCtor::Tuple { cardinality: cardinality as u16 },
258 TypeName::Raw(mutability) => TypeCtor::RawPtr(from_chalk(db, mutability)),
259 TypeName::Slice => TypeCtor::Slice,
260 TypeName::Ref(mutability) => TypeCtor::Ref(from_chalk(db, mutability)),
261 TypeName::Str => TypeCtor::Str,
262
263 TypeName::FnDef(_) => unreachable!(),
264
265 TypeName::Error => {
266 // this should not be reached, since we don't represent TypeName::Error with TypeCtor
267 unreachable!()
268 }
269 }
270 }
271}
272
273fn bitness_from_chalk_uint(uint_ty: chalk_ir::UintTy) -> IntBitness {
274 use chalk_ir::UintTy;
275
276 match uint_ty {
277 UintTy::Usize => IntBitness::Xsize,
278 UintTy::U8 => IntBitness::X8,
279 UintTy::U16 => IntBitness::X16,
280 UintTy::U32 => IntBitness::X32,
281 UintTy::U64 => IntBitness::X64,
282 UintTy::U128 => IntBitness::X128,
283 }
284}
285
286fn bitness_from_chalk_int(int_ty: chalk_ir::IntTy) -> IntBitness {
287 use chalk_ir::IntTy;
288
289 match int_ty {
290 IntTy::Isize => IntBitness::Xsize,
291 IntTy::I8 => IntBitness::X8,
292 IntTy::I16 => IntBitness::X16,
293 IntTy::I32 => IntBitness::X32,
294 IntTy::I64 => IntBitness::X64,
295 IntTy::I128 => IntBitness::X128,
296 }
297}
298
299fn int_ty_to_chalk(int_ty: IntTy) -> Scalar {
300 use chalk_ir::{IntTy, UintTy};
301
302 match int_ty.signedness {
303 Signedness::Signed => Scalar::Int(match int_ty.bitness {
304 IntBitness::Xsize => IntTy::Isize,
305 IntBitness::X8 => IntTy::I8,
306 IntBitness::X16 => IntTy::I16,
307 IntBitness::X32 => IntTy::I32,
308 IntBitness::X64 => IntTy::I64,
309 IntBitness::X128 => IntTy::I128,
310 }),
311 Signedness::Unsigned => Scalar::Uint(match int_ty.bitness {
312 IntBitness::Xsize => UintTy::Usize,
313 IntBitness::X8 => UintTy::U8,
314 IntBitness::X16 => UintTy::U16,
315 IntBitness::X32 => UintTy::U32,
316 IntBitness::X64 => UintTy::U64,
317 IntBitness::X128 => UintTy::U128,
318 }),
319 }
320}
321
322impl ToChalk for Mutability {
323 type Chalk = chalk_ir::Mutability;
324 fn to_chalk(self, _db: &dyn HirDatabase) -> Self::Chalk {
325 match self {
326 Mutability::Shared => chalk_ir::Mutability::Not,
327 Mutability::Mut => chalk_ir::Mutability::Mut,
328 }
329 }
330 fn from_chalk(_db: &dyn HirDatabase, chalk: Self::Chalk) -> Self {
331 match chalk {
332 chalk_ir::Mutability::Mut => Mutability::Mut,
333 chalk_ir::Mutability::Not => Mutability::Shared,
334 }
335 }
336}
337
338impl ToChalk for Impl {
339 type Chalk = ImplId;
340
341 fn to_chalk(self, db: &dyn HirDatabase) -> ImplId {
342 db.intern_chalk_impl(self).into()
343 }
344
345 fn from_chalk(db: &dyn HirDatabase, impl_id: ImplId) -> Impl {
346 db.lookup_intern_chalk_impl(impl_id.into())
347 }
348}
349
350impl ToChalk for TypeAliasId {
351 type Chalk = AssocTypeId;
352
353 fn to_chalk(self, _db: &dyn HirDatabase) -> AssocTypeId {
354 chalk_ir::AssocTypeId(self.as_intern_id())
355 }
356
357 fn from_chalk(_db: &dyn HirDatabase, type_alias_id: AssocTypeId) -> TypeAliasId {
358 InternKey::from_intern_id(type_alias_id.0)
359 }
360}
361
362impl ToChalk for AssocTyValue {
363 type Chalk = AssociatedTyValueId;
364
365 fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValueId {
366 db.intern_assoc_ty_value(self).into()
367 }
368
369 fn from_chalk(db: &dyn HirDatabase, assoc_ty_value_id: AssociatedTyValueId) -> AssocTyValue {
370 db.lookup_intern_assoc_ty_value(assoc_ty_value_id.into())
371 }
372}
373
374impl ToChalk for GenericPredicate {
375 type Chalk = chalk_ir::QuantifiedWhereClause<Interner>;
376
377 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::QuantifiedWhereClause<Interner> {
378 match self {
379 GenericPredicate::Implemented(trait_ref) => {
380 let chalk_trait_ref = trait_ref.to_chalk(db);
381 let chalk_trait_ref = chalk_trait_ref.shifted_in(&Interner);
382 make_binders(chalk_ir::WhereClause::Implemented(chalk_trait_ref), 0)
383 }
384 GenericPredicate::Projection(projection_pred) => {
385 let ty = projection_pred.ty.to_chalk(db).shifted_in(&Interner);
386 let projection = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner);
387 let alias = chalk_ir::AliasTy::Projection(projection);
388 make_binders(chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), 0)
389 }
390 GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"),
391 }
392 }
393
394 fn from_chalk(
395 db: &dyn HirDatabase,
396 where_clause: chalk_ir::QuantifiedWhereClause<Interner>,
397 ) -> GenericPredicate {
398 // we don't produce any where clauses with binders and can't currently deal with them
399 match where_clause
400 .skip_binders()
401 .shifted_out(&Interner)
402 .expect("unexpected bound vars in where clause")
403 {
404 chalk_ir::WhereClause::Implemented(tr) => {
405 GenericPredicate::Implemented(from_chalk(db, tr))
406 }
407 chalk_ir::WhereClause::AliasEq(projection_eq) => {
408 let projection_ty = from_chalk(
409 db,
410 match projection_eq.alias {
411 chalk_ir::AliasTy::Projection(p) => p,
412 _ => unimplemented!(),
413 },
414 );
415 let ty = from_chalk(db, projection_eq.ty);
416 GenericPredicate::Projection(ProjectionPredicate { projection_ty, ty })
417 }
418 }
419 }
420}
421
422impl ToChalk for ProjectionTy {
423 type Chalk = chalk_ir::ProjectionTy<Interner>;
424
425 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy<Interner> {
426 chalk_ir::ProjectionTy {
427 associated_ty_id: self.associated_ty.to_chalk(db),
428 substitution: self.parameters.to_chalk(db),
429 }
430 }
431
432 fn from_chalk(
433 db: &dyn HirDatabase,
434 projection_ty: chalk_ir::ProjectionTy<Interner>,
435 ) -> ProjectionTy {
436 ProjectionTy {
437 associated_ty: from_chalk(db, projection_ty.associated_ty_id),
438 parameters: from_chalk(db, projection_ty.substitution),
439 }
440 }
441}
442
443impl ToChalk for ProjectionPredicate {
444 type Chalk = chalk_ir::AliasEq<Interner>;
445
446 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> {
447 chalk_ir::AliasEq {
448 alias: chalk_ir::AliasTy::Projection(self.projection_ty.to_chalk(db)),
449 ty: self.ty.to_chalk(db),
450 }
451 }
452
453 fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq<Interner>) -> Self {
454 unimplemented!()
455 }
456}
457
458impl ToChalk for Obligation {
459 type Chalk = chalk_ir::DomainGoal<Interner>;
460
461 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::DomainGoal<Interner> {
462 match self {
463 Obligation::Trait(tr) => tr.to_chalk(db).cast(&Interner),
464 Obligation::Projection(pr) => pr.to_chalk(db).cast(&Interner),
465 }
466 }
467
468 fn from_chalk(_db: &dyn HirDatabase, _goal: chalk_ir::DomainGoal<Interner>) -> Self {
469 unimplemented!()
470 }
471}
472
473impl<T> ToChalk for Canonical<T>
474where
475 T: ToChalk,
476 T::Chalk: HasInterner<Interner = Interner>,
477{
478 type Chalk = chalk_ir::Canonical<T::Chalk>;
479
480 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> {
481 let parameter = chalk_ir::CanonicalVarKind::new(
482 chalk_ir::VariableKind::Ty,
483 chalk_ir::UniverseIndex::ROOT,
484 );
485 let value = self.value.to_chalk(db);
486 chalk_ir::Canonical {
487 value,
488 binders: chalk_ir::CanonicalVarKinds::from(&Interner, vec![parameter; self.num_vars]),
489 }
490 }
491
492 fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> {
493 Canonical {
494 num_vars: canonical.binders.len(&Interner),
495 value: from_chalk(db, canonical.value),
496 }
497 }
498}
499
500impl ToChalk for Arc<TraitEnvironment> {
501 type Chalk = chalk_ir::Environment<Interner>;
502
503 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Environment<Interner> {
504 let mut clauses = Vec::new();
505 for pred in &self.predicates {
506 if pred.is_error() {
507 // for env, we just ignore errors
508 continue;
509 }
510 let program_clause: chalk_ir::ProgramClause<Interner> =
511 pred.clone().to_chalk(db).cast(&Interner);
512 clauses.push(program_clause.into_from_env_clause(&Interner));
513 }
514 chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses)
515 }
516
517 fn from_chalk(
518 _db: &dyn HirDatabase,
519 _env: chalk_ir::Environment<Interner>,
520 ) -> Arc<TraitEnvironment> {
521 unimplemented!()
522 }
523}
524
525impl<T: ToChalk> ToChalk for InEnvironment<T>
526where
527 T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>,
528{
529 type Chalk = chalk_ir::InEnvironment<T::Chalk>;
530
531 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> {
532 chalk_ir::InEnvironment {
533 environment: self.environment.to_chalk(db),
534 goal: self.value.to_chalk(db),
535 }
536 }
537
538 fn from_chalk(
539 db: &dyn HirDatabase,
540 in_env: chalk_ir::InEnvironment<T::Chalk>,
541 ) -> InEnvironment<T> {
542 InEnvironment {
543 environment: from_chalk(db, in_env.environment),
544 value: from_chalk(db, in_env.goal),
545 }
546 }
547}
548
549impl ToChalk for builtin::BuiltinImplData {
550 type Chalk = ImplDatum;
551
552 fn to_chalk(self, db: &dyn HirDatabase) -> ImplDatum {
553 let impl_type = chalk_rust_ir::ImplType::External;
554 let where_clauses = self.where_clauses.into_iter().map(|w| w.to_chalk(db)).collect();
555
556 let impl_datum_bound =
557 chalk_rust_ir::ImplDatumBound { trait_ref: self.trait_ref.to_chalk(db), where_clauses };
558 let associated_ty_value_ids =
559 self.assoc_ty_values.into_iter().map(|v| v.to_chalk(db)).collect();
560 chalk_rust_ir::ImplDatum {
561 binders: make_binders(impl_datum_bound, self.num_vars),
562 impl_type,
563 polarity: chalk_rust_ir::Polarity::Positive,
564 associated_ty_value_ids,
565 }
566 }
567
568 fn from_chalk(_db: &dyn HirDatabase, _data: ImplDatum) -> Self {
569 unimplemented!()
570 }
571}
572
573impl ToChalk for builtin::BuiltinImplAssocTyValueData {
574 type Chalk = AssociatedTyValue;
575
576 fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValue {
577 let ty = self.value.to_chalk(db);
578 let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty };
579
580 chalk_rust_ir::AssociatedTyValue {
581 associated_ty_id: self.assoc_ty_id.to_chalk(db),
582 impl_id: self.impl_.to_chalk(db),
583 value: make_binders(value_bound, self.num_vars),
584 }
585 }
586
587 fn from_chalk(
588 _db: &dyn HirDatabase,
589 _data: AssociatedTyValue,
590 ) -> builtin::BuiltinImplAssocTyValueData {
591 unimplemented!()
592 }
593}
594
595pub(super) fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T>
596where
597 T: HasInterner<Interner = Interner>,
598{
599 chalk_ir::Binders::new(
600 chalk_ir::VariableKinds::from(
601 &Interner,
602 std::iter::repeat(chalk_ir::VariableKind::Ty).take(num_vars),
603 ),
604 value,
605 )
606}
607
608pub(super) fn convert_where_clauses(
609 db: &dyn HirDatabase,
610 def: GenericDefId,
611 substs: &Substs,
612) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
613 let generic_predicates = db.generic_predicates(def);
614 let mut result = Vec::with_capacity(generic_predicates.len());
615 for pred in generic_predicates.iter() {
616 if pred.value.is_error() {
617 // skip errored predicates completely
618 continue;
619 }
620 result.push(pred.clone().subst(substs).to_chalk(db));
621 }
622 result
623}
624
625pub(super) fn generic_predicate_to_inline_bound(
626 db: &dyn HirDatabase,
627 pred: &GenericPredicate,
628 self_ty: &Ty,
629) -> Option<chalk_rust_ir::InlineBound<Interner>> {
630 // An InlineBound is like a GenericPredicate, except the self type is left out.
631 // We don't have a special type for this, but Chalk does.
632 match pred {
633 GenericPredicate::Implemented(trait_ref) => {
634 if &trait_ref.substs[0] != self_ty {
635 // we can only convert predicates back to type bounds if they
636 // have the expected self type
637 return None;
638 }
639 let args_no_self = trait_ref.substs[1..]
640 .iter()
641 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
642 .collect();
643 let trait_bound =
644 chalk_rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self };
645 Some(chalk_rust_ir::InlineBound::TraitBound(trait_bound))
646 }
647 GenericPredicate::Projection(proj) => {
648 if &proj.projection_ty.parameters[0] != self_ty {
649 return None;
650 }
651 let trait_ = match proj.projection_ty.associated_ty.lookup(db.upcast()).container {
652 AssocContainerId::TraitId(t) => t,
653 _ => panic!("associated type not in trait"),
654 };
655 let args_no_self = proj.projection_ty.parameters[1..]
656 .iter()
657 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
658 .collect();
659 let alias_eq_bound = chalk_rust_ir::AliasEqBound {
660 value: proj.ty.clone().to_chalk(db),
661 trait_bound: chalk_rust_ir::TraitBound {
662 trait_id: trait_.to_chalk(db),
663 args_no_self,
664 },
665 associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db),
666 parameters: Vec::new(), // FIXME we don't support generic associated types yet
667 };
668 Some(chalk_rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
669 }
670 GenericPredicate::Error => None,
671 }
672}
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs
index 4867cb17e..ebf402a07 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,65 @@ 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 write!(fmt, "fn")
254 }
255
256 pub fn debug_const(
257 &self,
258 _constant: &chalk_ir::Const<Interner>,
259 fmt: &mut fmt::Formatter<'_>,
260 ) -> fmt::Result {
261 write!(fmt, "const")
262 }
263
264 pub fn debug_variable_kinds(
265 &self,
266 variable_kinds: &chalk_ir::VariableKinds<Interner>,
267 fmt: &mut fmt::Formatter<'_>,
268 ) -> fmt::Result {
269 write!(fmt, "{:?}", variable_kinds.as_slice(&Interner))
270 }
271 pub fn debug_variable_kinds_with_angles(
272 &self,
273 variable_kinds: &chalk_ir::VariableKinds<Interner>,
274 fmt: &mut fmt::Formatter<'_>,
275 ) -> fmt::Result {
276 write!(fmt, "{:?}", variable_kinds.inner_debug(&Interner))
277 }
278 pub fn debug_canonical_var_kinds(
279 &self,
280 canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Interner>,
281 fmt: &mut fmt::Formatter<'_>,
282 ) -> fmt::Result {
283 write!(fmt, "{:?}", canonical_var_kinds.as_slice(&Interner))
284 }
285 pub fn debug_program_clause(
286 &self,
287 clause: &chalk_ir::ProgramClause<Interner>,
288 fmt: &mut fmt::Formatter<'_>,
289 ) -> fmt::Result {
290 write!(fmt, "{:?}", clause.data(&Interner))
291 }
292 pub fn debug_program_clauses(
293 &self,
294 clauses: &chalk_ir::ProgramClauses<Interner>,
295 fmt: &mut fmt::Formatter<'_>,
296 ) -> fmt::Result {
297 write!(fmt, "{:?}", clauses.as_slice(&Interner))
298 }
299 pub fn debug_quantified_where_clauses(
300 &self,
301 clauses: &chalk_ir::QuantifiedWhereClauses<Interner>,
302 fmt: &mut fmt::Formatter<'_>,
303 ) -> fmt::Result {
304 write!(fmt, "{:?}", clauses.as_slice(&Interner))
305 }
247} 306}
248 307
249mod unsafe_tls { 308mod unsafe_tls {
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index c2819bbf7..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,22 +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 is_snippet: false, 631 ],
632 is_snippet: false,
633 },
632 }, 634 },
633 ), 635 ),
634 severity: Error,
635 }, 636 },
636 ] 637 ]
637 "###); 638 "###);
@@ -665,29 +666,31 @@ mod tests {
665 Diagnostic { 666 Diagnostic {
666 message: "Missing structure fields:\n- b", 667 message: "Missing structure fields:\n- b",
667 range: 224..233, 668 range: 224..233,
669 severity: Error,
668 fix: Some( 670 fix: Some(
669 SourceChange { 671 Fix {
670 label: "Fill struct fields", 672 label: "Fill struct fields",
671 source_file_edits: [ 673 source_change: SourceChange {
672 SourceFileEdit { 674 source_file_edits: [
673 file_id: FileId( 675 SourceFileEdit {
674 1, 676 file_id: FileId(
675 ), 677 1,
676 edit: TextEdit { 678 ),
677 indels: [ 679 edit: TextEdit {
678 Indel { 680 indels: [
679 insert: "{a:42, b: ()}", 681 Indel {
680 delete: 3..9, 682 insert: "{a:42, b: ()}",
681 }, 683 delete: 3..9,
682 ], 684 },
685 ],
686 },
683 }, 687 },
684 }, 688 ],
685 ], 689 file_system_edits: [],
686 file_system_edits: [], 690 is_snippet: false,
687 is_snippet: false, 691 },
688 }, 692 },
689 ), 693 ),
690 severity: Error,
691 }, 694 },
692 ] 695 ]
693 "###); 696 "###);
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 97ff67ee8..5ac002d82 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -97,8 +97,22 @@ pub type Cancelable<T> = Result<T, Canceled>;
97pub struct Diagnostic { 97pub struct Diagnostic {
98 pub message: String, 98 pub message: String,
99 pub range: TextRange, 99 pub range: TextRange,
100 pub fix: Option<SourceChange>,
101 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 }
102} 116}
103 117
104/// Info associated with a text range. 118/// Info associated with a text range.
@@ -493,7 +507,7 @@ impl Analysis {
493 ) -> Cancelable<Result<SourceChange, SsrError>> { 507 ) -> Cancelable<Result<SourceChange, SsrError>> {
494 self.with_db(|db| { 508 self.with_db(|db| {
495 let edits = ssr::parse_search_replace(query, parse_only, db)?; 509 let edits = ssr::parse_search_replace(query, parse_only, db)?;
496 Ok(SourceChange::source_file_edits("Structural Search Replace", edits)) 510 Ok(SourceChange::source_file_edits(edits))
497 }) 511 })
498 } 512 }
499 513
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index fd2163dad..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(
@@ -694,7 +693,6 @@ mod tests {
694 RangeInfo { 693 RangeInfo {
695 range: 4..7, 694 range: 4..7,
696 info: SourceChange { 695 info: SourceChange {
697 label: "Rename",
698 source_file_edits: [ 696 source_file_edits: [
699 SourceFileEdit { 697 SourceFileEdit {
700 file_id: FileId( 698 file_id: FileId(
@@ -777,7 +775,6 @@ mod tests {
777 RangeInfo { 775 RangeInfo {
778 range: 8..11, 776 range: 8..11,
779 info: SourceChange { 777 info: SourceChange {
780 label: "Rename",
781 source_file_edits: [ 778 source_file_edits: [
782 SourceFileEdit { 779 SourceFileEdit {
783 file_id: FileId( 780 file_id: FileId(
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 85be14ad3..e7d64b4f6 100644
--- a/crates/ra_ide/src/typing/on_enter.rs
+++ b/crates/ra_ide/src/typing/on_enter.rs
@@ -41,10 +41,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour
41 let inserted = format!("\n{}{} $0", indent, prefix); 41 let inserted = format!("\n{}{} $0", indent, prefix);
42 let edit = TextEdit::insert(position.offset, inserted); 42 let edit = TextEdit::insert(position.offset, inserted);
43 43
44 let mut res = SourceChange::source_file_edit( 44 let mut res = SourceChange::from(SourceFileEdit { edit, file_id: position.file_id });
45 "On enter",
46 SourceFileEdit { edit, file_id: position.file_id },
47 );
48 res.is_snippet = true; 45 res.is_snippet = true;
49 Some(res) 46 Some(res)
50} 47}
diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs
index 3484f5588..e713f4b7e 100644
--- a/crates/ra_ide_db/src/source_change.rs
+++ b/crates/ra_ide_db/src/source_change.rs
@@ -8,8 +8,6 @@ use 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 is_snippet: bool, 13 pub is_snippet: bool,
@@ -18,63 +16,22 @@ pub struct SourceChange {
18impl SourceChange { 16impl SourceChange {
19 /// Creates a new SourceChange with the given label 17 /// Creates a new SourceChange with the given label
20 /// from the edits. 18 /// from the edits.
21 pub fn from_edits<L: Into<String>>( 19 pub fn from_edits(
22 label: L,
23 source_file_edits: Vec<SourceFileEdit>, 20 source_file_edits: Vec<SourceFileEdit>,
24 file_system_edits: Vec<FileSystemEdit>, 21 file_system_edits: Vec<FileSystemEdit>,
25 ) -> Self { 22 ) -> Self {
26 SourceChange { 23 SourceChange { source_file_edits, file_system_edits, is_snippet: false }
27 label: label.into(),
28 source_file_edits,
29 file_system_edits,
30 is_snippet: false,
31 }
32 } 24 }
33 25
34 /// Creates a new SourceChange with the given label, 26 /// Creates a new SourceChange with the given label,
35 /// containing only the given `SourceFileEdits`. 27 /// containing only the given `SourceFileEdits`.
36 pub fn source_file_edits<L: Into<String>>(label: L, edits: Vec<SourceFileEdit>) -> Self { 28 pub fn source_file_edits(edits: Vec<SourceFileEdit>) -> Self {
37 let label = label.into(); 29 SourceChange { source_file_edits: edits, file_system_edits: vec![], is_snippet: false }
38 assert!(label.starts_with(char::is_uppercase));
39 SourceChange {
40 label: label,
41 source_file_edits: edits,
42 file_system_edits: vec![],
43 is_snippet: false,
44 }
45 }
46
47 /// Creates a new SourceChange with the given label,
48 /// containing only the given `FileSystemEdits`.
49 pub(crate) fn file_system_edits<L: Into<String>>(label: L, edits: Vec<FileSystemEdit>) -> Self {
50 SourceChange {
51 label: label.into(),
52 source_file_edits: vec![],
53 file_system_edits: edits,
54 is_snippet: false,
55 }
56 }
57
58 /// Creates a new SourceChange with the given label,
59 /// containing only a single `SourceFileEdit`.
60 pub fn source_file_edit<L: Into<String>>(label: L, edit: SourceFileEdit) -> Self {
61 SourceChange::source_file_edits(label, vec![edit])
62 }
63
64 /// Creates a new SourceChange with the given label
65 /// from the given `FileId` and `TextEdit`
66 pub fn source_file_edit_from<L: Into<String>>(
67 label: L,
68 file_id: FileId,
69 edit: TextEdit,
70 ) -> Self {
71 SourceChange::source_file_edit(label, SourceFileEdit { file_id, edit })
72 } 30 }
73
74 /// Creates a new SourceChange with the given label 31 /// Creates a new SourceChange with the given label
75 /// from the given `FileId` and `TextEdit` 32 /// from the given `FileId` and `TextEdit`
76 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 {
77 SourceChange::file_system_edits(label, vec![edit]) 34 SourceFileEdit { file_id, edit }.into()
78 } 35 }
79} 36}
80 37
@@ -84,23 +41,27 @@ pub struct SourceFileEdit {
84 pub edit: TextEdit, 41 pub edit: TextEdit,
85} 42}
86 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
87#[derive(Debug, Clone)] 54#[derive(Debug, Clone)]
88pub enum FileSystemEdit { 55pub enum FileSystemEdit {
89 CreateFile { source_root: SourceRootId, path: RelativePathBuf }, 56 CreateFile { source_root: SourceRootId, path: RelativePathBuf },
90 MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf }, 57 MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf },
91} 58}
92 59
93pub struct SingleFileChange { 60impl From<FileSystemEdit> for SourceChange {
94 pub label: String, 61 fn from(edit: FileSystemEdit) -> SourceChange {
95 pub edit: TextEdit,
96}
97
98impl SingleFileChange {
99 pub fn into_source_change(self, file_id: FileId) -> SourceChange {
100 SourceChange { 62 SourceChange {
101 label: self.label, 63 source_file_edits: Vec::new(),
102 source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }], 64 file_system_edits: vec![edit],
103 file_system_edits: Vec::new(),
104 is_snippet: false, 65 is_snippet: false,
105 } 66 }
106 } 67 }
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 4c417c270..780fc9317 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -2,19 +2,22 @@
2use std::env; 2use std::env;
3 3
4use lsp_types::{ 4use lsp_types::{
5 CallHierarchyServerCapability, CodeActionOptions, CodeActionProviderCapability, 5 CallHierarchyServerCapability, ClientCapabilities, CodeActionOptions,
6 CodeLensOptions, CompletionOptions, DocumentOnTypeFormattingOptions, 6 CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
7 FoldingRangeProviderCapability, ImplementationProviderCapability, RenameOptions, 7 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
8 RenameProviderCapability, SaveOptions, SelectionRangeProviderCapability, 8 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
9 SemanticTokensDocumentProvider, SemanticTokensLegend, SemanticTokensOptions, 9 SelectionRangeProviderCapability, SemanticTokensDocumentProvider, SemanticTokensLegend,
10 ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, 10 SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
11 TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions, 11 TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
12 WorkDoneProgressOptions,
12}; 13};
13use serde_json::json; 14use serde_json::json;
14 15
15use crate::semantic_tokens; 16use crate::semantic_tokens;
16 17
17pub fn server_capabilities() -> ServerCapabilities { 18pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities {
19 let code_action_provider = code_action_capabilities(client_caps);
20
18 ServerCapabilities { 21 ServerCapabilities {
19 text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { 22 text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
20 open_close: Some(true), 23 open_close: Some(true),
@@ -46,20 +49,7 @@ pub fn server_capabilities() -> ServerCapabilities {
46 document_highlight_provider: Some(true), 49 document_highlight_provider: Some(true),
47 document_symbol_provider: Some(true), 50 document_symbol_provider: Some(true),
48 workspace_symbol_provider: Some(true), 51 workspace_symbol_provider: Some(true),
49 code_action_provider: Some(CodeActionProviderCapability::Options(CodeActionOptions { 52 code_action_provider: Some(code_action_provider),
50 // Advertise support for all built-in CodeActionKinds
51 code_action_kinds: Some(vec![
52 lsp_types::code_action_kind::EMPTY.to_string(),
53 lsp_types::code_action_kind::QUICKFIX.to_string(),
54 lsp_types::code_action_kind::REFACTOR.to_string(),
55 lsp_types::code_action_kind::REFACTOR_EXTRACT.to_string(),
56 lsp_types::code_action_kind::REFACTOR_INLINE.to_string(),
57 lsp_types::code_action_kind::REFACTOR_REWRITE.to_string(),
58 lsp_types::code_action_kind::SOURCE.to_string(),
59 lsp_types::code_action_kind::SOURCE_ORGANIZE_IMPORTS.to_string(),
60 ]),
61 work_done_progress_options: Default::default(),
62 })),
63 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), 53 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
64 document_formatting_provider: Some(true), 54 document_formatting_provider: Some(true),
65 document_range_formatting_provider: None, 55 document_range_formatting_provider: None,
@@ -94,6 +84,33 @@ pub fn server_capabilities() -> ServerCapabilities {
94 ), 84 ),
95 experimental: Some(json!({ 85 experimental: Some(json!({
96 "joinLines": true, 86 "joinLines": true,
87 "ssr": true,
97 })), 88 })),
98 } 89 }
99} 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..0e4412ade 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 {
@@ -294,9 +295,13 @@ impl Config {
294 295
295 self.assist.allow_snippets(false); 296 self.assist.allow_snippets(false);
296 if let Some(experimental) = &caps.experimental { 297 if let Some(experimental) = &caps.experimental {
297 let enable = 298 let snippet_text_edit =
298 experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); 299 experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true);
299 self.assist.allow_snippets(enable); 300 self.assist.allow_snippets(snippet_text_edit);
301
302 let code_action_group =
303 experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true);
304 self.client_caps.code_action_group = code_action_group
300 } 305 }
301 } 306 }
302} 307}
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 1bb1b02ab..c25d90a50 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -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 121964718..a9703e1d6 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -18,7 +18,7 @@ use lsp_types::{
18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, 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 TextEdit,
23}; 23};
24use ra_prof::profile; 24use ra_prof::profile;
@@ -720,6 +720,7 @@ pub fn handle_code_action(
720 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)?;
721 let line_index = world.analysis().file_line_index(file_id)?; 721 let line_index = world.analysis().file_line_index(file_id)?;
722 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 };
723 724
724 let diagnostics = world.analysis().diagnostics(file_id)?; 725 let diagnostics = world.analysis().diagnostics(file_id)?;
725 let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); 726 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
@@ -730,10 +731,11 @@ pub fn handle_code_action(
730 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) 731 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some())
731 .map(|(_range, fix)| fix); 732 .map(|(_range, fix)| fix);
732 733
733 for source_edit in fixes_from_diagnostics { 734 for fix in fixes_from_diagnostics {
734 let title = source_edit.label.clone(); 735 let title = fix.label;
735 let edit = to_proto::snippet_workspace_edit(&world, source_edit)?; 736 let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?;
736 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 };
737 res.push(action); 739 res.push(action);
738 } 740 }
739 741
@@ -745,53 +747,9 @@ pub fn handle_code_action(
745 res.push(fix.action.clone()); 747 res.push(fix.action.clone());
746 } 748 }
747 749
748 let mut grouped_assists: FxHashMap<String, (usize, Vec<Assist>)> = FxHashMap::default(); 750 for assist in world.analysis().assists(&world.config.assist, frange)?.into_iter() {
749 for assist in 751 res.push(to_proto::code_action(&world, assist)?.into());
750 world.analysis().assists(&world.config.assist, FileRange { file_id, range })?.into_iter()
751 {
752 match &assist.group_label {
753 Some(label) => grouped_assists
754 .entry(label.to_owned())
755 .or_insert_with(|| {
756 let idx = res.len();
757 let dummy = lsp_ext::CodeAction {
758 title: String::new(),
759 kind: None,
760 command: None,
761 edit: None,
762 };
763 res.push(dummy);
764 (idx, Vec::new())
765 })
766 .1
767 .push(assist),
768 None => {
769 res.push(to_proto::code_action(&world, assist)?.into());
770 }
771 }
772 }
773
774 for (group_label, (idx, assists)) in grouped_assists {
775 if assists.len() == 1 {
776 res[idx] = to_proto::code_action(&world, assists.into_iter().next().unwrap())?.into();
777 } else {
778 let title = group_label;
779
780 let mut arguments = Vec::with_capacity(assists.len());
781 for assist in assists {
782 let source_change = to_proto::source_change(&world, assist.source_change)?;
783 arguments.push(to_value(source_change)?);
784 }
785
786 let command = Some(Command {
787 title: title.clone(),
788 command: "rust-analyzer.selectAndApplySourceChange".to_string(),
789 arguments: Some(vec![serde_json::Value::Array(arguments)]),
790 });
791 res[idx] = lsp_ext::CodeAction { title, kind: None, edit: None, command };
792 }
793 } 752 }
794
795 Ok(Some(res)) 753 Ok(Some(res))
796} 754}
797 755
@@ -986,11 +944,11 @@ pub fn handle_document_highlight(
986pub fn handle_ssr( 944pub fn handle_ssr(
987 world: WorldSnapshot, 945 world: WorldSnapshot,
988 params: lsp_ext::SsrParams, 946 params: lsp_ext::SsrParams,
989) -> Result<lsp_ext::SourceChange> { 947) -> Result<lsp_types::WorkspaceEdit> {
990 let _p = profile("handle_ssr"); 948 let _p = profile("handle_ssr");
991 let source_change = 949 let source_change =
992 world.analysis().structural_search_replace(&params.query, params.parse_only)??; 950 world.analysis().structural_search_replace(&params.query, params.parse_only)??;
993 to_proto::source_change(&world, source_change) 951 to_proto::workspace_edit(&world, source_change)
994} 952}
995 953
996pub 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 f6f4bb134..461944ada 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -478,15 +478,6 @@ pub(crate) fn resource_op(
478 Ok(res) 478 Ok(res)
479} 479}
480 480
481pub(crate) fn source_change(
482 world: &WorldSnapshot,
483 source_change: SourceChange,
484) -> Result<lsp_ext::SourceChange> {
485 let label = source_change.label.clone();
486 let workspace_edit = self::snippet_workspace_edit(world, source_change)?;
487 Ok(lsp_ext::SourceChange { label, workspace_edit, cursor_position: None })
488}
489
490pub(crate) fn snippet_workspace_edit( 481pub(crate) fn snippet_workspace_edit(
491 world: &WorldSnapshot, 482 world: &WorldSnapshot,
492 source_change: SourceChange, 483 source_change: SourceChange,
@@ -606,6 +597,7 @@ fn main() <fold>{
606pub(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> {
607 let res = lsp_ext::CodeAction { 598 let res = lsp_ext::CodeAction {
608 title: assist.label, 599 title: assist.label,
600 group: if world.config.client_caps.code_action_group { assist.group_label } else { None },
609 kind: Some(String::new()), 601 kind: Some(String::new()),
610 edit: Some(snippet_workspace_edit(world, assist.source_change)?), 602 edit: Some(snippet_workspace_edit(world, assist.source_change)?),
611 command: None, 603 command: None,
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 0e3a0af1c..158d3c599 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -5,7 +5,9 @@ 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
10**Issue:** https://github.com/microsoft/language-server-protocol/issues/724
9 11
10**Client Capability:** `{ "snippetTextEdit": boolean }` 12**Client Capability:** `{ "snippetTextEdit": boolean }`
11 13
@@ -36,7 +38,9 @@ At the moment, rust-analyzer guarantees that only a single edit will have `Inser
36* Where exactly are `SnippetTextEdit`s allowed (only in code actions at the moment)? 38* Where exactly are `SnippetTextEdit`s allowed (only in code actions at the moment)?
37* Can snippets span multiple files (so far, no)? 39* Can snippets span multiple files (so far, no)?
38 40
39## `joinLines` 41## Join Lines
42
43**Issue:** https://github.com/microsoft/language-server-protocol/issues/992
40 44
41**Server Capability:** `{ "joinLines": boolean }` 45**Server Capability:** `{ "joinLines": boolean }`
42 46
@@ -84,3 +88,85 @@ fn main() {
84* What is the position of the cursor after `joinLines`? 88* What is the position of the cursor after `joinLines`?
85 Currently this is left to editor's discretion, but it might be useful to specify on the server via snippets. 89 Currently this is left to editor's discretion, but it might be useful to specify on the server via snippets.
86 However, it then becomes unclear how it works with multi cursor. 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/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 e5ed77e32..abb53a248 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -41,15 +41,11 @@ 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
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 8b0a9d870..4d4513869 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -92,7 +92,7 @@ export async function activate(context: vscode.ExtensionContext) {
92 ctx.registerCommand('showReferences', commands.showReferences); 92 ctx.registerCommand('showReferences', commands.showReferences);
93 ctx.registerCommand('applySourceChange', commands.applySourceChange); 93 ctx.registerCommand('applySourceChange', commands.applySourceChange);
94 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); 94 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand);
95 ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); 95 ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
96 96
97 ctx.pushCleanup(activateTaskProvider(workspaceFolder)); 97 ctx.pushCleanup(activateTaskProvider(workspaceFolder));
98 98
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts
index 8ed56c173..73f36432f 100644
--- a/editors/code/src/rust-analyzer-api.ts
+++ b/editors/code/src/rust-analyzer-api.ts
@@ -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");