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