aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/assist_config.rs27
-rw-r--r--crates/ra_assists/src/assist_context.rs58
-rw-r--r--crates/ra_assists/src/handlers/add_custom_impl.rs51
-rw-r--r--crates/ra_assists/src/handlers/add_derive.rs28
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs23
-rw-r--r--crates/ra_assists/src/handlers/add_from_impl_for_enum.rs21
-rw-r--r--crates/ra_assists/src/handlers/add_function.rs145
-rw-r--r--crates/ra_assists/src/handlers/add_impl.rs34
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs98
-rw-r--r--crates/ra_assists/src/handlers/add_new.rs56
-rw-r--r--crates/ra_assists/src/handlers/add_turbo_fish.rs134
-rw-r--r--crates/ra_assists/src/handlers/apply_demorgan.rs8
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs29
-rw-r--r--crates/ra_assists/src/handlers/change_return_type_to_result.rs98
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs548
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs18
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs232
-rw-r--r--crates/ra_assists/src/handlers/fix_visibility.rs559
-rw-r--r--crates/ra_assists/src/handlers/flip_binexpr.rs14
-rw-r--r--crates/ra_assists/src/handlers/flip_comma.rs2
-rw-r--r--crates/ra_assists/src/handlers/flip_trait_bound.rs14
-rw-r--r--crates/ra_assists/src/handlers/inline_local_variable.rs63
-rw-r--r--crates/ra_assists/src/handlers/introduce_variable.rs128
-rw-r--r--crates/ra_assists/src/handlers/invert_if.rs6
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs22
-rw-r--r--crates/ra_assists/src/handlers/merge_match_arms.rs25
-rw-r--r--crates/ra_assists/src/handlers/move_bounds.rs8
-rw-r--r--crates/ra_assists/src/handlers/move_guard.rs33
-rw-r--r--crates/ra_assists/src/handlers/raw_string.rs26
-rw-r--r--crates/ra_assists/src/handlers/remove_dbg.rs39
-rw-r--r--crates/ra_assists/src/handlers/remove_mut.rs5
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs8
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs29
-rw-r--r--crates/ra_assists/src/handlers/replace_let_with_if_let.rs5
-rw-r--r--crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs40
-rw-r--r--crates/ra_assists/src/handlers/replace_unwrap_with_match.rs43
-rw-r--r--crates/ra_assists/src/handlers/split_import.rs6
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs34
-rw-r--r--crates/ra_assists/src/lib.rs20
-rw-r--r--crates/ra_assists/src/marks.rs12
-rw-r--r--crates/ra_assists/src/tests.rs30
-rw-r--r--crates/ra_assists/src/tests/generated.rs68
-rw-r--r--crates/ra_assists/src/utils.rs78
-rw-r--r--crates/ra_assists/src/utils/insert_use.rs6
-rw-r--r--crates/ra_hir_def/src/body/lower.rs4
-rw-r--r--crates/ra_hir_def/src/body/scope.rs4
-rw-r--r--crates/ra_hir_def/src/db.rs6
-rw-r--r--crates/ra_hir_def/src/find_path.rs100
-rw-r--r--crates/ra_hir_def/src/lang_item.rs12
-rw-r--r--crates/ra_hir_def/src/lib.rs2
-rw-r--r--crates/ra_hir_def/src/marks.rs17
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs14
-rw-r--r--crates/ra_hir_def/src/nameres/path_resolution.rs8
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs4
-rw-r--r--crates/ra_hir_def/src/nameres/tests.rs8
-rw-r--r--crates/ra_hir_def/src/nameres/tests/globs.rs7
-rw-r--r--crates/ra_hir_def/src/nameres/tests/macros.rs9
-rw-r--r--crates/ra_hir_def/src/nameres/tests/mod_resolution.rs2
-rw-r--r--crates/ra_hir_def/src/path/lower/lower_use.rs4
-rw-r--r--crates/ra_hir_ty/Cargo.toml6
-rw-r--r--crates/ra_hir_ty/src/_match.rs39
-rw-r--r--crates/ra_hir_ty/src/db.rs7
-rw-r--r--crates/ra_hir_ty/src/infer.rs1
-rw-r--r--crates/ra_hir_ty/src/infer/coerce.rs6
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs33
-rw-r--r--crates/ra_hir_ty/src/infer/pat.rs4
-rw-r--r--crates/ra_hir_ty/src/infer/unify.rs8
-rw-r--r--crates/ra_hir_ty/src/lib.rs9
-rw-r--r--crates/ra_hir_ty/src/lower.rs2
-rw-r--r--crates/ra_hir_ty/src/marks.rs12
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs2
-rw-r--r--crates/ra_hir_ty/src/tests/coercion.rs6
-rw-r--r--crates/ra_hir_ty/src/tests/method_resolution.rs2
-rw-r--r--crates/ra_hir_ty/src/tests/patterns.rs4
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs15
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs63
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs160
-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_info.rs8
-rw-r--r--crates/ra_ide/src/completion.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs8
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs8
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs4
-rw-r--r--crates/ra_ide/src/completion/presentation.rs24
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs2
-rw-r--r--crates/ra_ide/src/diagnostics.rs101
-rw-r--r--crates/ra_ide/src/display.rs6
-rw-r--r--crates/ra_ide/src/goto_definition.rs10
-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.rs43
-rw-r--r--crates/ra_ide/src/marks.rs16
-rw-r--r--crates/ra_ide/src/parent_module.rs8
-rw-r--r--crates/ra_ide/src/references.rs3
-rw-r--r--crates/ra_ide/src/references/rename.rs33
-rw-r--r--crates/ra_ide/src/runnables.rs70
-rw-r--r--crates/ra_ide/src/test_utils.rs25
-rw-r--r--crates/ra_ide/src/typing.rs54
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs26
-rw-r--r--crates/ra_ide_db/src/defs.rs10
-rw-r--r--crates/ra_ide_db/src/lib.rs2
-rw-r--r--crates/ra_ide_db/src/line_index_utils.rs302
-rw-r--r--crates/ra_ide_db/src/marks.rs12
-rw-r--r--crates/ra_ide_db/src/search.rs2
-rw-r--r--crates/ra_ide_db/src/source_change.rs100
-rw-r--r--crates/ra_syntax/Cargo.toml2
-rw-r--r--crates/ra_syntax/src/algo.rs36
-rw-r--r--crates/ra_syntax/src/ast/edit.rs44
-rw-r--r--crates/ra_syntax/src/ast/make.rs36
-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/cli/analysis_bench.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs23
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs10
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap7
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap7
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs21
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs75
-rw-r--r--crates/rust-analyzer/src/main_loop.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs164
-rw-r--r--crates/rust-analyzer/src/to_proto.rs162
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs122
-rw-r--r--crates/stdx/src/lib.rs8
-rw-r--r--crates/test_utils/src/lib.rs2
-rw-r--r--crates/test_utils/src/mark.rs (renamed from crates/test_utils/src/marks.rs)46
131 files changed, 4029 insertions, 3364 deletions
diff --git a/crates/ra_assists/src/assist_config.rs b/crates/ra_assists/src/assist_config.rs
new file mode 100644
index 000000000..c0a0226fb
--- /dev/null
+++ b/crates/ra_assists/src/assist_config.rs
@@ -0,0 +1,27 @@
1//! Settings for tweaking assists.
2//!
3//! The fun thing here is `SnippetCap` -- this type can only be created in this
4//! module, and we use to statically check that we only produce snippet
5//! assists if we are allowed to.
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct AssistConfig {
9 pub snippet_cap: Option<SnippetCap>,
10}
11
12impl AssistConfig {
13 pub fn allow_snippets(&mut self, yes: bool) {
14 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
15 }
16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct SnippetCap {
20 _private: (),
21}
22
23impl Default for AssistConfig {
24 fn default() -> Self {
25 AssistConfig { snippet_cap: Some(SnippetCap { _private: () }) }
26 }
27}
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index a680f752b..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::{
@@ -15,7 +15,10 @@ use ra_syntax::{
15}; 15};
16use ra_text_edit::TextEditBuilder; 16use ra_text_edit::TextEditBuilder;
17 17
18use crate::{Assist, AssistId, GroupLabel, ResolvedAssist}; 18use crate::{
19 assist_config::{AssistConfig, SnippetCap},
20 Assist, AssistId, GroupLabel, ResolvedAssist,
21};
19 22
20/// `AssistContext` allows to apply an assist or check if it could be applied. 23/// `AssistContext` allows to apply an assist or check if it could be applied.
21/// 24///
@@ -48,6 +51,7 @@ use crate::{Assist, AssistId, GroupLabel, ResolvedAssist};
48/// moment, because the LSP API is pretty awkward in this place, and it's much 51/// moment, because the LSP API is pretty awkward in this place, and it's much
49/// easier to just compute the edit eagerly :-) 52/// easier to just compute the edit eagerly :-)
50pub(crate) struct AssistContext<'a> { 53pub(crate) struct AssistContext<'a> {
54 pub(crate) config: &'a AssistConfig,
51 pub(crate) sema: Semantics<'a, RootDatabase>, 55 pub(crate) sema: Semantics<'a, RootDatabase>,
52 pub(crate) db: &'a RootDatabase, 56 pub(crate) db: &'a RootDatabase,
53 pub(crate) frange: FileRange, 57 pub(crate) frange: FileRange,
@@ -55,10 +59,14 @@ pub(crate) struct AssistContext<'a> {
55} 59}
56 60
57impl<'a> AssistContext<'a> { 61impl<'a> AssistContext<'a> {
58 pub fn new(sema: Semantics<'a, RootDatabase>, frange: FileRange) -> AssistContext<'a> { 62 pub(crate) fn new(
63 sema: Semantics<'a, RootDatabase>,
64 config: &'a AssistConfig,
65 frange: FileRange,
66 ) -> AssistContext<'a> {
59 let source_file = sema.parse(frange.file_id); 67 let source_file = sema.parse(frange.file_id);
60 let db = sema.db; 68 let db = sema.db;
61 AssistContext { sema, db, frange, source_file } 69 AssistContext { config, sema, db, frange, source_file }
62 } 70 }
63 71
64 // NB, this ignores active selection. 72 // NB, this ignores active selection.
@@ -142,11 +150,10 @@ impl Assists {
142 self.add_impl(label, f) 150 self.add_impl(label, f)
143 } 151 }
144 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<()> {
145 let change_label = label.label.clone();
146 let source_change = if self.resolve { 153 let source_change = if self.resolve {
147 let mut builder = AssistBuilder::new(self.file); 154 let mut builder = AssistBuilder::new(self.file);
148 f(&mut builder); 155 f(&mut builder);
149 Some(builder.finish(change_label)) 156 Some(builder.finish())
150 } else { 157 } else {
151 None 158 None
152 }; 159 };
@@ -163,13 +170,13 @@ impl Assists {
163 170
164pub(crate) struct AssistBuilder { 171pub(crate) struct AssistBuilder {
165 edit: TextEditBuilder, 172 edit: TextEditBuilder,
166 cursor_position: Option<TextSize>,
167 file: FileId, 173 file: FileId,
174 is_snippet: bool,
168} 175}
169 176
170impl AssistBuilder { 177impl AssistBuilder {
171 pub(crate) fn new(file: FileId) -> AssistBuilder { 178 pub(crate) fn new(file: FileId) -> AssistBuilder {
172 AssistBuilder { edit: TextEditBuilder::default(), cursor_position: None, file } 179 AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false }
173 } 180 }
174 181
175 /// Remove specified `range` of text. 182 /// Remove specified `range` of text.
@@ -180,10 +187,30 @@ impl AssistBuilder {
180 pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) { 187 pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) {
181 self.edit.insert(offset, text.into()) 188 self.edit.insert(offset, text.into())
182 } 189 }
190 /// Append specified `snippet` at the given `offset`
191 pub(crate) fn insert_snippet(
192 &mut self,
193 _cap: SnippetCap,
194 offset: TextSize,
195 snippet: impl Into<String>,
196 ) {
197 self.is_snippet = true;
198 self.insert(offset, snippet);
199 }
183 /// Replaces specified `range` of text with a given string. 200 /// Replaces specified `range` of text with a given string.
184 pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { 201 pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
185 self.edit.replace(range, replace_with.into()) 202 self.edit.replace(range, replace_with.into())
186 } 203 }
204 /// Replaces specified `range` of text with a given `snippet`.
205 pub(crate) fn replace_snippet(
206 &mut self,
207 _cap: SnippetCap,
208 range: TextRange,
209 snippet: impl Into<String>,
210 ) {
211 self.is_snippet = true;
212 self.replace(range, snippet);
213 }
187 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { 214 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
188 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) 215 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
189 } 216 }
@@ -207,10 +234,6 @@ impl AssistBuilder {
207 algo::diff(&node, &new).into_text_edit(&mut self.edit) 234 algo::diff(&node, &new).into_text_edit(&mut self.edit)
208 } 235 }
209 236
210 /// Specify desired position of the cursor after the assist is applied.
211 pub(crate) fn set_cursor(&mut self, offset: TextSize) {
212 self.cursor_position = Some(offset)
213 }
214 // FIXME: better API 237 // FIXME: better API
215 pub(crate) fn set_file(&mut self, assist_file: FileId) { 238 pub(crate) fn set_file(&mut self, assist_file: FileId) {
216 self.file = assist_file; 239 self.file = assist_file;
@@ -222,12 +245,13 @@ impl AssistBuilder {
222 &mut self.edit 245 &mut self.edit
223 } 246 }
224 247
225 fn finish(self, change_label: String) -> SourceChange { 248 fn finish(self) -> SourceChange {
226 let edit = self.edit.finish(); 249 let edit = self.edit.finish();
227 if edit.is_empty() && self.cursor_position.is_none() { 250 let source_file_edit = SourceFileEdit { file_id: self.file, edit };
228 panic!("Only call `add_assist` if the assist can be applied") 251 let mut res: SourceChange = source_file_edit.into();
252 if self.is_snippet {
253 res.is_snippet = true;
229 } 254 }
230 SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position } 255 res
231 .into_source_change(self.file)
232 } 256 }
233} 257}
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs
index 2baeb8607..fa70c8496 100644
--- a/crates/ra_assists/src/handlers/add_custom_impl.rs
+++ b/crates/ra_assists/src/handlers/add_custom_impl.rs
@@ -25,7 +25,7 @@ use crate::{
25// struct S; 25// struct S;
26// 26//
27// impl Debug for S { 27// impl Debug for S {
28// 28// $0
29// } 29// }
30// ``` 30// ```
31pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 31pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
@@ -52,7 +52,7 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
52 format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name); 52 format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name);
53 53
54 let target = attr.syntax().text_range(); 54 let target = attr.syntax().text_range();
55 acc.add(AssistId("add_custom_impl"), label, target, |edit| { 55 acc.add(AssistId("add_custom_impl"), label, target, |builder| {
56 let new_attr_input = input 56 let new_attr_input = input
57 .syntax() 57 .syntax()
58 .descendants_with_tokens() 58 .descendants_with_tokens()
@@ -63,20 +63,11 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
63 let has_more_derives = !new_attr_input.is_empty(); 63 let has_more_derives = !new_attr_input.is_empty();
64 let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string(); 64 let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string();
65 65
66 let mut buf = String::new(); 66 if has_more_derives {
67 buf.push_str("\n\nimpl "); 67 builder.replace(input.syntax().text_range(), new_attr_input);
68 buf.push_str(trait_token.text().as_str());
69 buf.push_str(" for ");
70 buf.push_str(annotated_name.as_str());
71 buf.push_str(" {\n");
72
73 let cursor_delta = if has_more_derives {
74 let delta = input.syntax().text_range().len() - TextSize::of(&new_attr_input);
75 edit.replace(input.syntax().text_range(), new_attr_input);
76 delta
77 } else { 68 } else {
78 let attr_range = attr.syntax().text_range(); 69 let attr_range = attr.syntax().text_range();
79 edit.delete(attr_range); 70 builder.delete(attr_range);
80 71
81 let line_break_range = attr 72 let line_break_range = attr
82 .syntax() 73 .syntax()
@@ -84,14 +75,24 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
84 .filter(|t| t.kind() == WHITESPACE) 75 .filter(|t| t.kind() == WHITESPACE)
85 .map(|t| t.text_range()) 76 .map(|t| t.text_range())
86 .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); 77 .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0)));
87 edit.delete(line_break_range); 78 builder.delete(line_break_range);
88 79 }
89 attr_range.len() + line_break_range.len() 80
90 }; 81 match ctx.config.snippet_cap {
91 82 Some(cap) => {
92 edit.set_cursor(start_offset + TextSize::of(&buf) - cursor_delta); 83 builder.insert_snippet(
93 buf.push_str("\n}"); 84 cap,
94 edit.insert(start_offset, buf); 85 start_offset,
86 format!("\n\nimpl {} for {} {{\n $0\n}}", trait_token, annotated_name),
87 );
88 }
89 None => {
90 builder.insert(
91 start_offset,
92 format!("\n\nimpl {} for {} {{\n\n}}", trait_token, annotated_name),
93 );
94 }
95 }
95 }) 96 })
96} 97}
97 98
@@ -117,7 +118,7 @@ struct Foo {
117} 118}
118 119
119impl Debug for Foo { 120impl Debug for Foo {
120<|> 121 $0
121} 122}
122 ", 123 ",
123 ) 124 )
@@ -139,7 +140,7 @@ pub struct Foo {
139} 140}
140 141
141impl Debug for Foo { 142impl Debug for Foo {
142<|> 143 $0
143} 144}
144 ", 145 ",
145 ) 146 )
@@ -158,7 +159,7 @@ struct Foo {}
158struct Foo {} 159struct Foo {}
159 160
160impl Debug for Foo { 161impl Debug for Foo {
161<|> 162 $0
162} 163}
163 ", 164 ",
164 ) 165 )
diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs
index fb08c19e9..b123b8498 100644
--- a/crates/ra_assists/src/handlers/add_derive.rs
+++ b/crates/ra_assists/src/handlers/add_derive.rs
@@ -18,31 +18,37 @@ use crate::{AssistContext, AssistId, Assists};
18// ``` 18// ```
19// -> 19// ->
20// ``` 20// ```
21// #[derive()] 21// #[derive($0)]
22// struct Point { 22// struct Point {
23// x: u32, 23// x: u32,
24// y: u32, 24// y: u32,
25// } 25// }
26// ``` 26// ```
27pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 27pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
28 let cap = ctx.config.snippet_cap?;
28 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
29 let node_start = derive_insertion_offset(&nominal)?; 30 let node_start = derive_insertion_offset(&nominal)?;
30 let target = nominal.syntax().text_range(); 31 let target = nominal.syntax().text_range();
31 acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |edit| { 32 acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |builder| {
32 let derive_attr = nominal 33 let derive_attr = nominal
33 .attrs() 34 .attrs()
34 .filter_map(|x| x.as_simple_call()) 35 .filter_map(|x| x.as_simple_call())
35 .filter(|(name, _arg)| name == "derive") 36 .filter(|(name, _arg)| name == "derive")
36 .map(|(_name, arg)| arg) 37 .map(|(_name, arg)| arg)
37 .next(); 38 .next();
38 let offset = match derive_attr { 39 match derive_attr {
39 None => { 40 None => {
40 edit.insert(node_start, "#[derive()]\n"); 41 builder.insert_snippet(cap, node_start, "#[derive($0)]\n");
41 node_start + TextSize::of("#[derive(") 42 }
43 Some(tt) => {
44 // Just move the cursor.
45 builder.insert_snippet(
46 cap,
47 tt.syntax().text_range().end() - TextSize::of(')'),
48 "$0",
49 )
42 } 50 }
43 Some(tt) => tt.syntax().text_range().end() - TextSize::of(')'),
44 }; 51 };
45 edit.set_cursor(offset)
46 }) 52 })
47} 53}
48 54
@@ -66,12 +72,12 @@ mod tests {
66 check_assist( 72 check_assist(
67 add_derive, 73 add_derive,
68 "struct Foo { a: i32, <|>}", 74 "struct Foo { a: i32, <|>}",
69 "#[derive(<|>)]\nstruct Foo { a: i32, }", 75 "#[derive($0)]\nstruct Foo { a: i32, }",
70 ); 76 );
71 check_assist( 77 check_assist(
72 add_derive, 78 add_derive,
73 "struct Foo { <|> a: i32, }", 79 "struct Foo { <|> a: i32, }",
74 "#[derive(<|>)]\nstruct Foo { a: i32, }", 80 "#[derive($0)]\nstruct Foo { a: i32, }",
75 ); 81 );
76 } 82 }
77 83
@@ -80,7 +86,7 @@ mod tests {
80 check_assist( 86 check_assist(
81 add_derive, 87 add_derive,
82 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", 88 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
83 "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", 89 "#[derive(Clone$0)]\nstruct Foo { a: i32, }",
84 ); 90 );
85 } 91 }
86 92
@@ -96,7 +102,7 @@ struct Foo { a: i32<|>, }
96 " 102 "
97/// `Foo` is a pretty important struct. 103/// `Foo` is a pretty important struct.
98/// It does stuff. 104/// It does stuff.
99#[derive(<|>)] 105#[derive($0)]
100struct Foo { a: i32, } 106struct Foo { a: i32, }
101 ", 107 ",
102 ); 108 );
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index 0c7d5e355..ab20c6649 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -25,9 +25,8 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
25 let stmt = ctx.find_node_at_offset::<LetStmt>()?; 25 let stmt = ctx.find_node_at_offset::<LetStmt>()?;
26 let module = ctx.sema.scope(stmt.syntax()).module()?; 26 let module = ctx.sema.scope(stmt.syntax()).module()?;
27 let expr = stmt.initializer()?; 27 let expr = stmt.initializer()?;
28 let pat = stmt.pat()?;
29 // Must be a binding 28 // Must be a binding
30 let pat = match pat { 29 let pat = match stmt.pat()? {
31 ast::Pat::BindPat(bind_pat) => bind_pat, 30 ast::Pat::BindPat(bind_pat) => bind_pat,
32 _ => return None, 31 _ => return None,
33 }; 32 };
@@ -46,7 +45,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
46 // Assist not applicable if the type has already been specified 45 // Assist not applicable if the type has already been specified
47 // and it has no placeholders 46 // and it has no placeholders
48 let ascribed_ty = stmt.ascribed_type(); 47 let ascribed_ty = stmt.ascribed_type();
49 if let Some(ref ty) = ascribed_ty { 48 if let Some(ty) = &ascribed_ty {
50 if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() { 49 if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() {
51 return None; 50 return None;
52 } 51 }
@@ -87,11 +86,7 @@ mod tests {
87 86
88 #[test] 87 #[test]
89 fn add_explicit_type_works_for_simple_expr() { 88 fn add_explicit_type_works_for_simple_expr() {
90 check_assist( 89 check_assist(add_explicit_type, "fn f() { let a<|> = 1; }", "fn f() { let a: i32 = 1; }");
91 add_explicit_type,
92 "fn f() { let a<|> = 1; }",
93 "fn f() { let a<|>: i32 = 1; }",
94 );
95 } 90 }
96 91
97 #[test] 92 #[test]
@@ -99,7 +94,7 @@ mod tests {
99 check_assist( 94 check_assist(
100 add_explicit_type, 95 add_explicit_type,
101 "fn f() { let a<|>: _ = 1; }", 96 "fn f() { let a<|>: _ = 1; }",
102 "fn f() { let a<|>: i32 = 1; }", 97 "fn f() { let a: i32 = 1; }",
103 ); 98 );
104 } 99 }
105 100
@@ -123,7 +118,7 @@ mod tests {
123 } 118 }
124 119
125 fn f() { 120 fn f() {
126 let a<|>: Option<i32> = Option::Some(1); 121 let a: Option<i32> = Option::Some(1);
127 }"#, 122 }"#,
128 ); 123 );
129 } 124 }
@@ -133,7 +128,7 @@ mod tests {
133 check_assist( 128 check_assist(
134 add_explicit_type, 129 add_explicit_type,
135 r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }", 130 r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }",
136 r"macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }", 131 r"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }",
137 ); 132 );
138 } 133 }
139 134
@@ -141,8 +136,8 @@ mod tests {
141 fn add_explicit_type_works_for_macro_call_recursive() { 136 fn add_explicit_type_works_for_macro_call_recursive() {
142 check_assist( 137 check_assist(
143 add_explicit_type, 138 add_explicit_type,
144 "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }", 139 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }"#,
145 "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }", 140 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a: u64 = v!(); }"#,
146 ); 141 );
147 } 142 }
148 143
@@ -209,7 +204,7 @@ struct Test<K, T = u8> {
209} 204}
210 205
211fn main() { 206fn main() {
212 let test<|>: Test<i32> = Test { t: 23, k: 33 }; 207 let test: Test<i32> = Test { t: 23, k: 33 };
213}"#, 208}"#,
214 ); 209 );
215 } 210 }
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
index 6a49b7dbd..6a675e812 100644
--- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
+++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
@@ -1,7 +1,6 @@
1use ra_ide_db::RootDatabase; 1use ra_ide_db::RootDatabase;
2use ra_syntax::ast::{self, AstNode, NameOwner}; 2use ra_syntax::ast::{self, AstNode, NameOwner};
3use stdx::format_to; 3use test_utils::mark;
4use test_utils::tested_by;
5 4
6use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; 5use crate::{utils::FamousDefs, AssistContext, AssistId, Assists};
7 6
@@ -35,12 +34,12 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) ->
35 } 34 }
36 let field_type = field_list.fields().next()?.type_ref()?; 35 let field_type = field_list.fields().next()?.type_ref()?;
37 let path = match field_type { 36 let path = match field_type {
38 ast::TypeRef::PathType(p) => p, 37 ast::TypeRef::PathType(it) => it,
39 _ => return None, 38 _ => return None,
40 }; 39 };
41 40
42 if existing_from_impl(&ctx.sema, &variant).is_some() { 41 if existing_from_impl(&ctx.sema, &variant).is_some() {
43 tested_by!(test_add_from_impl_already_exists); 42 mark::hit!(test_add_from_impl_already_exists);
44 return None; 43 return None;
45 } 44 }
46 45
@@ -51,9 +50,7 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) ->
51 target, 50 target,
52 |edit| { 51 |edit| {
53 let start_offset = variant.parent_enum().syntax().text_range().end(); 52 let start_offset = variant.parent_enum().syntax().text_range().end();
54 let mut buf = String::new(); 53 let buf = format!(
55 format_to!(
56 buf,
57 r#" 54 r#"
58 55
59impl From<{0}> for {1} {{ 56impl From<{0}> for {1} {{
@@ -93,7 +90,7 @@ fn existing_from_impl(
93 90
94#[cfg(test)] 91#[cfg(test)]
95mod tests { 92mod tests {
96 use test_utils::covers; 93 use test_utils::mark;
97 94
98 use crate::tests::{check_assist, check_assist_not_applicable}; 95 use crate::tests::{check_assist, check_assist_not_applicable};
99 96
@@ -104,7 +101,7 @@ mod tests {
104 check_assist( 101 check_assist(
105 add_from_impl_for_enum, 102 add_from_impl_for_enum,
106 "enum A { <|>One(u32) }", 103 "enum A { <|>One(u32) }",
107 r#"enum A { <|>One(u32) } 104 r#"enum A { One(u32) }
108 105
109impl From<u32> for A { 106impl From<u32> for A {
110 fn from(v: u32) -> Self { 107 fn from(v: u32) -> Self {
@@ -119,7 +116,7 @@ impl From<u32> for A {
119 check_assist( 116 check_assist(
120 add_from_impl_for_enum, 117 add_from_impl_for_enum,
121 r#"enum A { <|>One(foo::bar::baz::Boo) }"#, 118 r#"enum A { <|>One(foo::bar::baz::Boo) }"#,
122 r#"enum A { <|>One(foo::bar::baz::Boo) } 119 r#"enum A { One(foo::bar::baz::Boo) }
123 120
124impl From<foo::bar::baz::Boo> for A { 121impl From<foo::bar::baz::Boo> for A {
125 fn from(v: foo::bar::baz::Boo) -> Self { 122 fn from(v: foo::bar::baz::Boo) -> Self {
@@ -152,7 +149,7 @@ impl From<foo::bar::baz::Boo> for A {
152 149
153 #[test] 150 #[test]
154 fn test_add_from_impl_already_exists() { 151 fn test_add_from_impl_already_exists() {
155 covers!(test_add_from_impl_already_exists); 152 mark::check!(test_add_from_impl_already_exists);
156 check_not_applicable( 153 check_not_applicable(
157 r#" 154 r#"
158enum A { <|>One(u32), } 155enum A { <|>One(u32), }
@@ -181,7 +178,7 @@ impl From<String> for A {
181pub trait From<T> { 178pub trait From<T> {
182 fn from(T) -> Self; 179 fn from(T) -> Self;
183}"#, 180}"#,
184 r#"enum A { <|>One(u32), Two(String), } 181 r#"enum A { One(u32), Two(String), }
185 182
186impl From<u32> for A { 183impl From<u32> for A {
187 fn from(v: u32) -> Self { 184 fn from(v: u32) -> Self {
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs
index de016ae4e..24f931a85 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/add_function.rs
@@ -4,13 +4,17 @@ use ra_syntax::{
4 ast::{ 4 ast::{
5 self, 5 self,
6 edit::{AstNodeEdit, IndentLevel}, 6 edit::{AstNodeEdit, IndentLevel},
7 ArgListOwner, AstNode, ModuleItemOwner, 7 make, ArgListOwner, AstNode, ModuleItemOwner,
8 }, 8 },
9 SyntaxKind, SyntaxNode, TextSize, 9 SyntaxKind, SyntaxNode, TextSize,
10}; 10};
11use rustc_hash::{FxHashMap, FxHashSet}; 11use rustc_hash::{FxHashMap, FxHashSet};
12 12
13use crate::{AssistContext, AssistId, Assists}; 13use crate::{
14 assist_config::SnippetCap,
15 utils::{render_snippet, Cursor},
16 AssistContext, AssistId, Assists,
17};
14 18
15// Assist: add_function 19// Assist: add_function
16// 20//
@@ -33,7 +37,7 @@ use crate::{AssistContext, AssistId, Assists};
33// } 37// }
34// 38//
35// fn bar(arg: &str, baz: Baz) { 39// fn bar(arg: &str, baz: Baz) {
36// todo!() 40// ${0:todo!()}
37// } 41// }
38// 42//
39// ``` 43// ```
@@ -58,21 +62,40 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
58 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; 62 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
59 63
60 let target = call.syntax().text_range(); 64 let target = call.syntax().text_range();
61 acc.add(AssistId("add_function"), "Add function", target, |edit| { 65 acc.add(AssistId("add_function"), "Add function", target, |builder| {
62 let function_template = function_builder.render(); 66 let function_template = function_builder.render();
63 edit.set_file(function_template.file); 67 builder.set_file(function_template.file);
64 edit.set_cursor(function_template.cursor_offset); 68 let new_fn = function_template.to_string(ctx.config.snippet_cap);
65 edit.insert(function_template.insert_offset, function_template.fn_def.to_string()); 69 match ctx.config.snippet_cap {
70 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn),
71 None => builder.insert(function_template.insert_offset, new_fn),
72 }
66 }) 73 })
67} 74}
68 75
69struct FunctionTemplate { 76struct FunctionTemplate {
70 insert_offset: TextSize, 77 insert_offset: TextSize,
71 cursor_offset: TextSize, 78 placeholder_expr: ast::MacroCall,
72 fn_def: ast::SourceFile, 79 leading_ws: String,
80 fn_def: ast::FnDef,
81 trailing_ws: String,
73 file: FileId, 82 file: FileId,
74} 83}
75 84
85impl FunctionTemplate {
86 fn to_string(&self, cap: Option<SnippetCap>) -> String {
87 let f = match cap {
88 Some(cap) => render_snippet(
89 cap,
90 self.fn_def.syntax(),
91 Cursor::Replace(self.placeholder_expr.syntax()),
92 ),
93 None => self.fn_def.to_string(),
94 };
95 format!("{}{}{}", self.leading_ws, f, self.trailing_ws)
96 }
97}
98
76struct FunctionBuilder { 99struct FunctionBuilder {
77 target: GeneratedFunctionTarget, 100 target: GeneratedFunctionTarget,
78 fn_name: ast::Name, 101 fn_name: ast::Name,
@@ -110,35 +133,41 @@ impl FunctionBuilder {
110 } 133 }
111 134
112 fn render(self) -> FunctionTemplate { 135 fn render(self) -> FunctionTemplate {
113 let placeholder_expr = ast::make::expr_todo(); 136 let placeholder_expr = make::expr_todo();
114 let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); 137 let fn_body = make::block_expr(vec![], Some(placeholder_expr));
115 let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); 138 let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None };
116 if self.needs_pub { 139 let mut fn_def =
117 fn_def = ast::make::add_pub_crate_modifier(fn_def); 140 make::fn_def(visibility, self.fn_name, self.type_params, self.params, fn_body);
118 } 141 let leading_ws;
119 142 let trailing_ws;
120 let (fn_def, insert_offset) = match self.target { 143
144 let insert_offset = match self.target {
121 GeneratedFunctionTarget::BehindItem(it) => { 145 GeneratedFunctionTarget::BehindItem(it) => {
122 let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); 146 let indent = IndentLevel::from_node(&it);
123 let indented = with_leading_blank_line.indent(IndentLevel::from_node(&it)); 147 leading_ws = format!("\n\n{}", indent);
124 (indented, it.text_range().end()) 148 fn_def = fn_def.indent(indent);
149 trailing_ws = String::new();
150 it.text_range().end()
125 } 151 }
126 GeneratedFunctionTarget::InEmptyItemList(it) => { 152 GeneratedFunctionTarget::InEmptyItemList(it) => {
127 let indent_once = IndentLevel(1);
128 let indent = IndentLevel::from_node(it.syntax()); 153 let indent = IndentLevel::from_node(it.syntax());
129 let fn_def = ast::make::add_leading_newlines(1, fn_def); 154 leading_ws = format!("\n{}", indent + 1);
130 let fn_def = fn_def.indent(indent_once); 155 fn_def = fn_def.indent(indent + 1);
131 let fn_def = ast::make::add_trailing_newlines(1, fn_def); 156 trailing_ws = format!("\n{}", indent);
132 let fn_def = fn_def.indent(indent); 157 it.syntax().text_range().start() + TextSize::of('{')
133 (fn_def, it.syntax().text_range().start() + TextSize::of('{'))
134 } 158 }
135 }; 159 };
136 160
137 let placeholder_expr = 161 let placeholder_expr =
138 fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); 162 fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
139 let cursor_offset_from_fn_start = placeholder_expr.syntax().text_range().start(); 163 FunctionTemplate {
140 let cursor_offset = insert_offset + cursor_offset_from_fn_start; 164 insert_offset,
141 FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file } 165 placeholder_expr,
166 leading_ws,
167 fn_def,
168 trailing_ws,
169 file: self.file,
170 }
142 } 171 }
143} 172}
144 173
@@ -158,7 +187,7 @@ impl GeneratedFunctionTarget {
158 187
159fn fn_name(call: &ast::Path) -> Option<ast::Name> { 188fn fn_name(call: &ast::Path) -> Option<ast::Name> {
160 let name = call.segment()?.syntax().to_string(); 189 let name = call.segment()?.syntax().to_string();
161 Some(ast::make::name(&name)) 190 Some(make::name(&name))
162} 191}
163 192
164/// Computes the type variables and arguments required for the generated function 193/// Computes the type variables and arguments required for the generated function
@@ -180,8 +209,8 @@ fn fn_args(
180 }); 209 });
181 } 210 }
182 deduplicate_arg_names(&mut arg_names); 211 deduplicate_arg_names(&mut arg_names);
183 let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| ast::make::param(name, ty)); 212 let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| make::param(name, ty));
184 Some((None, ast::make::param_list(params))) 213 Some((None, make::param_list(params)))
185} 214}
186 215
187/// Makes duplicate argument names unique by appending incrementing numbers. 216/// Makes duplicate argument names unique by appending incrementing numbers.
@@ -316,7 +345,7 @@ fn foo() {
316} 345}
317 346
318fn bar() { 347fn bar() {
319 <|>todo!() 348 ${0:todo!()}
320} 349}
321", 350",
322 ) 351 )
@@ -343,7 +372,7 @@ impl Foo {
343} 372}
344 373
345fn bar() { 374fn bar() {
346 <|>todo!() 375 ${0:todo!()}
347} 376}
348", 377",
349 ) 378 )
@@ -367,7 +396,7 @@ fn foo1() {
367} 396}
368 397
369fn bar() { 398fn bar() {
370 <|>todo!() 399 ${0:todo!()}
371} 400}
372 401
373fn foo2() {} 402fn foo2() {}
@@ -393,7 +422,7 @@ mod baz {
393 } 422 }
394 423
395 fn bar() { 424 fn bar() {
396 <|>todo!() 425 ${0:todo!()}
397 } 426 }
398} 427}
399", 428",
@@ -419,7 +448,7 @@ fn foo() {
419} 448}
420 449
421fn bar(baz: Baz) { 450fn bar(baz: Baz) {
422 <|>todo!() 451 ${0:todo!()}
423} 452}
424", 453",
425 ); 454 );
@@ -452,7 +481,7 @@ impl Baz {
452} 481}
453 482
454fn bar(baz: Baz) { 483fn bar(baz: Baz) {
455 <|>todo!() 484 ${0:todo!()}
456} 485}
457", 486",
458 ) 487 )
@@ -473,7 +502,7 @@ fn foo() {
473} 502}
474 503
475fn bar(arg: &str) { 504fn bar(arg: &str) {
476 <|>todo!() 505 ${0:todo!()}
477} 506}
478"#, 507"#,
479 ) 508 )
@@ -494,7 +523,7 @@ fn foo() {
494} 523}
495 524
496fn bar(arg: char) { 525fn bar(arg: char) {
497 <|>todo!() 526 ${0:todo!()}
498} 527}
499"#, 528"#,
500 ) 529 )
@@ -515,7 +544,7 @@ fn foo() {
515} 544}
516 545
517fn bar(arg: i32) { 546fn bar(arg: i32) {
518 <|>todo!() 547 ${0:todo!()}
519} 548}
520", 549",
521 ) 550 )
@@ -536,7 +565,7 @@ fn foo() {
536} 565}
537 566
538fn bar(arg: u8) { 567fn bar(arg: u8) {
539 <|>todo!() 568 ${0:todo!()}
540} 569}
541", 570",
542 ) 571 )
@@ -561,7 +590,7 @@ fn foo() {
561} 590}
562 591
563fn bar(x: u8) { 592fn bar(x: u8) {
564 <|>todo!() 593 ${0:todo!()}
565} 594}
566", 595",
567 ) 596 )
@@ -584,7 +613,7 @@ fn foo() {
584} 613}
585 614
586fn bar(worble: ()) { 615fn bar(worble: ()) {
587 <|>todo!() 616 ${0:todo!()}
588} 617}
589", 618",
590 ) 619 )
@@ -613,7 +642,7 @@ fn baz() {
613} 642}
614 643
615fn bar(foo: impl Foo) { 644fn bar(foo: impl Foo) {
616 <|>todo!() 645 ${0:todo!()}
617} 646}
618", 647",
619 ) 648 )
@@ -640,7 +669,7 @@ fn foo() {
640} 669}
641 670
642fn bar(baz: &Baz) { 671fn bar(baz: &Baz) {
643 <|>todo!() 672 ${0:todo!()}
644} 673}
645", 674",
646 ) 675 )
@@ -669,7 +698,7 @@ fn foo() {
669} 698}
670 699
671fn bar(baz: Baz::Bof) { 700fn bar(baz: Baz::Bof) {
672 <|>todo!() 701 ${0:todo!()}
673} 702}
674", 703",
675 ) 704 )
@@ -692,7 +721,7 @@ fn foo<T>(t: T) {
692} 721}
693 722
694fn bar<T>(t: T) { 723fn bar<T>(t: T) {
695 <|>todo!() 724 ${0:todo!()}
696} 725}
697", 726",
698 ) 727 )
@@ -723,7 +752,7 @@ fn foo() {
723} 752}
724 753
725fn bar(arg: fn() -> Baz) { 754fn bar(arg: fn() -> Baz) {
726 <|>todo!() 755 ${0:todo!()}
727} 756}
728", 757",
729 ) 758 )
@@ -748,7 +777,7 @@ fn foo() {
748} 777}
749 778
750fn bar(closure: impl Fn(i64) -> i64) { 779fn bar(closure: impl Fn(i64) -> i64) {
751 <|>todo!() 780 ${0:todo!()}
752} 781}
753", 782",
754 ) 783 )
@@ -769,7 +798,7 @@ fn foo() {
769} 798}
770 799
771fn bar(baz: ()) { 800fn bar(baz: ()) {
772 <|>todo!() 801 ${0:todo!()}
773} 802}
774", 803",
775 ) 804 )
@@ -794,7 +823,7 @@ fn foo() {
794} 823}
795 824
796fn bar(baz_1: Baz, baz_2: Baz) { 825fn bar(baz_1: Baz, baz_2: Baz) {
797 <|>todo!() 826 ${0:todo!()}
798} 827}
799", 828",
800 ) 829 )
@@ -819,7 +848,7 @@ fn foo() {
819} 848}
820 849
821fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { 850fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) {
822 <|>todo!() 851 ${0:todo!()}
823} 852}
824"#, 853"#,
825 ) 854 )
@@ -839,7 +868,7 @@ fn foo() {
839 r" 868 r"
840mod bar { 869mod bar {
841 pub(crate) fn my_fn() { 870 pub(crate) fn my_fn() {
842 <|>todo!() 871 ${0:todo!()}
843 } 872 }
844} 873}
845 874
@@ -878,7 +907,7 @@ fn bar() {
878} 907}
879 908
880fn baz(foo: foo::Foo) { 909fn baz(foo: foo::Foo) {
881 <|>todo!() 910 ${0:todo!()}
882} 911}
883", 912",
884 ) 913 )
@@ -902,7 +931,7 @@ mod bar {
902 fn something_else() {} 931 fn something_else() {}
903 932
904 pub(crate) fn my_fn() { 933 pub(crate) fn my_fn() {
905 <|>todo!() 934 ${0:todo!()}
906 } 935 }
907} 936}
908 937
@@ -930,7 +959,7 @@ fn foo() {
930mod bar { 959mod bar {
931 mod baz { 960 mod baz {
932 pub(crate) fn my_fn() { 961 pub(crate) fn my_fn() {
933 <|>todo!() 962 ${0:todo!()}
934 } 963 }
935 } 964 }
936} 965}
@@ -959,7 +988,7 @@ fn main() {
959 988
960 989
961pub(crate) fn bar() { 990pub(crate) fn bar() {
962 <|>todo!() 991 ${0:todo!()}
963}", 992}",
964 ) 993 )
965 } 994 }
diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs
index df114a0d8..eceba7d0a 100644
--- a/crates/ra_assists/src/handlers/add_impl.rs
+++ b/crates/ra_assists/src/handlers/add_impl.rs
@@ -1,7 +1,4 @@
1use ra_syntax::{ 1use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
2 ast::{self, AstNode, NameOwner, TypeParamsOwner},
3 TextSize,
4};
5use stdx::{format_to, SepBy}; 2use stdx::{format_to, SepBy};
6 3
7use crate::{AssistContext, AssistId, Assists}; 4use crate::{AssistContext, AssistId, Assists};
@@ -12,17 +9,17 @@ use crate::{AssistContext, AssistId, Assists};
12// 9//
13// ``` 10// ```
14// struct Ctx<T: Clone> { 11// struct Ctx<T: Clone> {
15// data: T,<|> 12// data: T,<|>
16// } 13// }
17// ``` 14// ```
18// -> 15// ->
19// ``` 16// ```
20// struct Ctx<T: Clone> { 17// struct Ctx<T: Clone> {
21// data: T, 18// data: T,
22// } 19// }
23// 20//
24// impl<T: Clone> Ctx<T> { 21// impl<T: Clone> Ctx<T> {
25// 22// $0
26// } 23// }
27// ``` 24// ```
28pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 25pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
@@ -50,30 +47,37 @@ pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
50 let generic_params = lifetime_params.chain(type_params).sep_by(", "); 47 let generic_params = lifetime_params.chain(type_params).sep_by(", ");
51 format_to!(buf, "<{}>", generic_params) 48 format_to!(buf, "<{}>", generic_params)
52 } 49 }
53 buf.push_str(" {\n"); 50 match ctx.config.snippet_cap {
54 edit.set_cursor(start_offset + TextSize::of(&buf)); 51 Some(cap) => {
55 buf.push_str("\n}"); 52 buf.push_str(" {\n $0\n}");
56 edit.insert(start_offset, buf); 53 edit.insert_snippet(cap, start_offset, buf);
54 }
55 None => {
56 buf.push_str(" {\n}");
57 edit.insert(start_offset, buf);
58 }
59 }
57 }) 60 })
58} 61}
59 62
60#[cfg(test)] 63#[cfg(test)]
61mod tests { 64mod tests {
62 use super::*;
63 use crate::tests::{check_assist, check_assist_target}; 65 use crate::tests::{check_assist, check_assist_target};
64 66
67 use super::*;
68
65 #[test] 69 #[test]
66 fn test_add_impl() { 70 fn test_add_impl() {
67 check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n<|>\n}\n"); 71 check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n $0\n}\n");
68 check_assist( 72 check_assist(
69 add_impl, 73 add_impl,
70 "struct Foo<T: Clone> {<|>}", 74 "struct Foo<T: Clone> {<|>}",
71 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}", 75 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}",
72 ); 76 );
73 check_assist( 77 check_assist(
74 add_impl, 78 add_impl,
75 "struct Foo<'a, T: Foo<'a>> {<|>}", 79 "struct Foo<'a, T: Foo<'a>> {<|>}",
76 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}", 80 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}",
77 ); 81 );
78 } 82 }
79 83
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index 22e1156d2..abacd4065 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11use crate::{ 11use crate::{
12 assist_context::{AssistContext, Assists}, 12 assist_context::{AssistContext, Assists},
13 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, 13 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
14 utils::{get_missing_assoc_items, resolve_target_trait}, 14 utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor},
15 AssistId, 15 AssistId,
16}; 16};
17 17
@@ -46,7 +46,7 @@ enum AddMissingImplMembersMode {
46// 46//
47// impl Trait<u32> for () { 47// impl Trait<u32> for () {
48// fn foo(&self) -> u32 { 48// fn foo(&self) -> u32 {
49// todo!() 49// ${0:todo!()}
50// } 50// }
51// 51//
52// } 52// }
@@ -89,7 +89,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -
89// impl Trait for () { 89// impl Trait for () {
90// Type X = (); 90// Type X = ();
91// fn foo(&self) {} 91// fn foo(&self) {}
92// fn bar(&self) {} 92// $0fn bar(&self) {}
93// 93//
94// } 94// }
95// ``` 95// ```
@@ -147,7 +147,7 @@ fn add_missing_impl_members_inner(
147 } 147 }
148 148
149 let target = impl_def.syntax().text_range(); 149 let target = impl_def.syntax().text_range();
150 acc.add(AssistId(assist_id), label, target, |edit| { 150 acc.add(AssistId(assist_id), label, target, |builder| {
151 let n_existing_items = impl_item_list.assoc_items().count(); 151 let n_existing_items = impl_item_list.assoc_items().count();
152 let source_scope = ctx.sema.scope_for_def(trait_); 152 let source_scope = ctx.sema.scope_for_def(trait_);
153 let target_scope = ctx.sema.scope(impl_item_list.syntax()); 153 let target_scope = ctx.sema.scope(impl_item_list.syntax());
@@ -162,13 +162,29 @@ fn add_missing_impl_members_inner(
162 }) 162 })
163 .map(|it| edit::remove_attrs_and_docs(&it)); 163 .map(|it| edit::remove_attrs_and_docs(&it));
164 let new_impl_item_list = impl_item_list.append_items(items); 164 let new_impl_item_list = impl_item_list.append_items(items);
165 let cursor_position = { 165 let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap();
166 let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap(); 166
167 first_new_item.syntax().text_range().start() 167 let original_range = impl_item_list.syntax().text_range();
168 match ctx.config.snippet_cap {
169 None => builder.replace(original_range, new_impl_item_list.to_string()),
170 Some(cap) => {
171 let mut cursor = Cursor::Before(first_new_item.syntax());
172 let placeholder;
173 if let ast::AssocItem::FnDef(func) = &first_new_item {
174 if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) {
175 if m.syntax().text() == "todo!()" {
176 placeholder = m;
177 cursor = Cursor::Replace(placeholder.syntax());
178 }
179 }
180 }
181 builder.replace_snippet(
182 cap,
183 original_range,
184 render_snippet(cap, new_impl_item_list.syntax(), cursor),
185 )
186 }
168 }; 187 };
169
170 edit.replace_ast(impl_item_list, new_impl_item_list);
171 edit.set_cursor(cursor_position);
172 }) 188 })
173} 189}
174 190
@@ -222,7 +238,7 @@ struct S;
222 238
223impl Foo for S { 239impl Foo for S {
224 fn bar(&self) {} 240 fn bar(&self) {}
225 <|>type Output; 241 $0type Output;
226 const CONST: usize = 42; 242 const CONST: usize = 42;
227 fn foo(&self) { 243 fn foo(&self) {
228 todo!() 244 todo!()
@@ -263,8 +279,8 @@ struct S;
263 279
264impl Foo for S { 280impl Foo for S {
265 fn bar(&self) {} 281 fn bar(&self) {}
266 <|>fn foo(&self) { 282 fn foo(&self) {
267 todo!() 283 ${0:todo!()}
268 } 284 }
269 285
270}"#, 286}"#,
@@ -283,8 +299,8 @@ impl Foo for S { <|> }"#,
283trait Foo { fn foo(&self); } 299trait Foo { fn foo(&self); }
284struct S; 300struct S;
285impl Foo for S { 301impl Foo for S {
286 <|>fn foo(&self) { 302 fn foo(&self) {
287 todo!() 303 ${0:todo!()}
288 } 304 }
289}"#, 305}"#,
290 ); 306 );
@@ -302,8 +318,8 @@ impl Foo<u32> for S { <|> }"#,
302trait Foo<T> { fn foo(&self, t: T) -> &T; } 318trait Foo<T> { fn foo(&self, t: T) -> &T; }
303struct S; 319struct S;
304impl Foo<u32> for S { 320impl Foo<u32> for S {
305 <|>fn foo(&self, t: u32) -> &u32 { 321 fn foo(&self, t: u32) -> &u32 {
306 todo!() 322 ${0:todo!()}
307 } 323 }
308}"#, 324}"#,
309 ); 325 );
@@ -321,8 +337,8 @@ impl<U> Foo<U> for S { <|> }"#,
321trait Foo<T> { fn foo(&self, t: T) -> &T; } 337trait Foo<T> { fn foo(&self, t: T) -> &T; }
322struct S; 338struct S;
323impl<U> Foo<U> for S { 339impl<U> Foo<U> for S {
324 <|>fn foo(&self, t: U) -> &U { 340 fn foo(&self, t: U) -> &U {
325 todo!() 341 ${0:todo!()}
326 } 342 }
327}"#, 343}"#,
328 ); 344 );
@@ -340,8 +356,8 @@ impl Foo for S {}<|>"#,
340trait Foo { fn foo(&self); } 356trait Foo { fn foo(&self); }
341struct S; 357struct S;
342impl Foo for S { 358impl Foo for S {
343 <|>fn foo(&self) { 359 fn foo(&self) {
344 todo!() 360 ${0:todo!()}
345 } 361 }
346}"#, 362}"#,
347 ) 363 )
@@ -365,8 +381,8 @@ mod foo {
365} 381}
366struct S; 382struct S;
367impl foo::Foo for S { 383impl foo::Foo for S {
368 <|>fn foo(&self, bar: foo::Bar) { 384 fn foo(&self, bar: foo::Bar) {
369 todo!() 385 ${0:todo!()}
370 } 386 }
371}"#, 387}"#,
372 ); 388 );
@@ -390,8 +406,8 @@ mod foo {
390} 406}
391struct S; 407struct S;
392impl foo::Foo for S { 408impl foo::Foo for S {
393 <|>fn foo(&self, bar: foo::Bar<u32>) { 409 fn foo(&self, bar: foo::Bar<u32>) {
394 todo!() 410 ${0:todo!()}
395 } 411 }
396}"#, 412}"#,
397 ); 413 );
@@ -415,8 +431,8 @@ mod foo {
415} 431}
416struct S; 432struct S;
417impl foo::Foo<u32> for S { 433impl foo::Foo<u32> for S {
418 <|>fn foo(&self, bar: foo::Bar<u32>) { 434 fn foo(&self, bar: foo::Bar<u32>) {
419 todo!() 435 ${0:todo!()}
420 } 436 }
421}"#, 437}"#,
422 ); 438 );
@@ -443,8 +459,8 @@ mod foo {
443struct Param; 459struct Param;
444struct S; 460struct S;
445impl foo::Foo<Param> for S { 461impl foo::Foo<Param> for S {
446 <|>fn foo(&self, bar: Param) { 462 fn foo(&self, bar: Param) {
447 todo!() 463 ${0:todo!()}
448 } 464 }
449}"#, 465}"#,
450 ); 466 );
@@ -470,8 +486,8 @@ mod foo {
470} 486}
471struct S; 487struct S;
472impl foo::Foo for S { 488impl foo::Foo for S {
473 <|>fn foo(&self, bar: foo::Bar<u32>::Assoc) { 489 fn foo(&self, bar: foo::Bar<u32>::Assoc) {
474 todo!() 490 ${0:todo!()}
475 } 491 }
476}"#, 492}"#,
477 ); 493 );
@@ -497,8 +513,8 @@ mod foo {
497} 513}
498struct S; 514struct S;
499impl foo::Foo for S { 515impl foo::Foo for S {
500 <|>fn foo(&self, bar: foo::Bar<foo::Baz>) { 516 fn foo(&self, bar: foo::Bar<foo::Baz>) {
501 todo!() 517 ${0:todo!()}
502 } 518 }
503}"#, 519}"#,
504 ); 520 );
@@ -522,8 +538,8 @@ mod foo {
522} 538}
523struct S; 539struct S;
524impl foo::Foo for S { 540impl foo::Foo for S {
525 <|>fn foo(&self, bar: dyn Fn(u32) -> i32) { 541 fn foo(&self, bar: dyn Fn(u32) -> i32) {
526 todo!() 542 ${0:todo!()}
527 } 543 }
528}"#, 544}"#,
529 ); 545 );
@@ -580,7 +596,7 @@ trait Foo {
580} 596}
581struct S; 597struct S;
582impl Foo for S { 598impl Foo for S {
583 <|>type Output; 599 $0type Output;
584 fn foo(&self) { 600 fn foo(&self) {
585 todo!() 601 todo!()
586 } 602 }
@@ -614,7 +630,7 @@ trait Foo {
614} 630}
615struct S; 631struct S;
616impl Foo for S { 632impl Foo for S {
617 <|>fn valid(some: u32) -> bool { false } 633 $0fn valid(some: u32) -> bool { false }
618}"#, 634}"#,
619 ) 635 )
620 } 636 }
@@ -637,8 +653,8 @@ trait Foo<T = Self> {
637 653
638struct S; 654struct S;
639impl Foo for S { 655impl Foo for S {
640 <|>fn bar(&self, other: &Self) { 656 fn bar(&self, other: &Self) {
641 todo!() 657 ${0:todo!()}
642 } 658 }
643}"#, 659}"#,
644 ) 660 )
@@ -662,8 +678,8 @@ trait Foo<T1, T2 = Self> {
662 678
663struct S<T>; 679struct S<T>;
664impl Foo<T> for S<T> { 680impl Foo<T> for S<T> {
665 <|>fn bar(&self, this: &T, that: &Self) { 681 fn bar(&self, this: &T, that: &Self) {
666 todo!() 682 ${0:todo!()}
667 } 683 }
668}"#, 684}"#,
669 ) 685 )
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs
index fe7451dcf..837aa8377 100644
--- a/crates/ra_assists/src/handlers/add_new.rs
+++ b/crates/ra_assists/src/handlers/add_new.rs
@@ -3,7 +3,7 @@ use ra_syntax::{
3 ast::{ 3 ast::{
4 self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner, 4 self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner,
5 }, 5 },
6 TextSize, T, 6 T,
7}; 7};
8use stdx::{format_to, SepBy}; 8use stdx::{format_to, SepBy};
9 9
@@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists};
25// } 25// }
26// 26//
27// impl<T: Clone> Ctx<T> { 27// impl<T: Clone> Ctx<T> {
28// fn new(data: T) -> Self { Self { data } } 28// fn $0new(data: T) -> Self { Self { data } }
29// } 29// }
30// 30//
31// ``` 31// ```
@@ -42,31 +42,26 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
42 let impl_def = find_struct_impl(&ctx, &strukt)?; 42 let impl_def = find_struct_impl(&ctx, &strukt)?;
43 43
44 let target = strukt.syntax().text_range(); 44 let target = strukt.syntax().text_range();
45 acc.add(AssistId("add_new"), "Add default constructor", target, |edit| { 45 acc.add(AssistId("add_new"), "Add default constructor", target, |builder| {
46 let mut buf = String::with_capacity(512); 46 let mut buf = String::with_capacity(512);
47 47
48 if impl_def.is_some() { 48 if impl_def.is_some() {
49 buf.push('\n'); 49 buf.push('\n');
50 } 50 }
51 51
52 let vis = strukt.visibility().map(|v| format!("{} ", v)); 52 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
53 let vis = vis.as_deref().unwrap_or("");
54 53
55 let params = field_list 54 let params = field_list
56 .fields() 55 .fields()
57 .filter_map(|f| { 56 .filter_map(|f| {
58 Some(format!( 57 Some(format!("{}: {}", f.name()?.syntax(), f.ascribed_type()?.syntax()))
59 "{}: {}",
60 f.name()?.syntax().text(),
61 f.ascribed_type()?.syntax().text()
62 ))
63 }) 58 })
64 .sep_by(", "); 59 .sep_by(", ");
65 let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", "); 60 let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");
66 61
67 format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields); 62 format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
68 63
69 let (start_offset, end_offset) = impl_def 64 let start_offset = impl_def
70 .and_then(|impl_def| { 65 .and_then(|impl_def| {
71 buf.push('\n'); 66 buf.push('\n');
72 let start = impl_def 67 let start = impl_def
@@ -76,17 +71,20 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
76 .text_range() 71 .text_range()
77 .end(); 72 .end();
78 73
79 Some((start, TextSize::of("\n"))) 74 Some(start)
80 }) 75 })
81 .unwrap_or_else(|| { 76 .unwrap_or_else(|| {
82 buf = generate_impl_text(&strukt, &buf); 77 buf = generate_impl_text(&strukt, &buf);
83 let start = strukt.syntax().text_range().end(); 78 strukt.syntax().text_range().end()
84
85 (start, TextSize::of("\n}\n"))
86 }); 79 });
87 80
88 edit.set_cursor(start_offset + TextSize::of(&buf) - end_offset); 81 match ctx.config.snippet_cap {
89 edit.insert(start_offset, buf); 82 None => builder.insert(start_offset, buf),
83 Some(cap) => {
84 buf = buf.replace("fn new", "fn $0new");
85 builder.insert_snippet(cap, start_offset, buf);
86 }
87 }
90 }) 88 })
91} 89}
92 90
@@ -191,7 +189,7 @@ mod tests {
191"struct Foo {} 189"struct Foo {}
192 190
193impl Foo { 191impl Foo {
194 fn new() -> Self { Self { } }<|> 192 fn $0new() -> Self { Self { } }
195} 193}
196", 194",
197 ); 195 );
@@ -201,7 +199,7 @@ impl Foo {
201"struct Foo<T: Clone> {} 199"struct Foo<T: Clone> {}
202 200
203impl<T: Clone> Foo<T> { 201impl<T: Clone> Foo<T> {
204 fn new() -> Self { Self { } }<|> 202 fn $0new() -> Self { Self { } }
205} 203}
206", 204",
207 ); 205 );
@@ -211,7 +209,7 @@ impl<T: Clone> Foo<T> {
211"struct Foo<'a, T: Foo<'a>> {} 209"struct Foo<'a, T: Foo<'a>> {}
212 210
213impl<'a, T: Foo<'a>> Foo<'a, T> { 211impl<'a, T: Foo<'a>> Foo<'a, T> {
214 fn new() -> Self { Self { } }<|> 212 fn $0new() -> Self { Self { } }
215} 213}
216", 214",
217 ); 215 );
@@ -221,7 +219,7 @@ impl<'a, T: Foo<'a>> Foo<'a, T> {
221"struct Foo { baz: String } 219"struct Foo { baz: String }
222 220
223impl Foo { 221impl Foo {
224 fn new(baz: String) -> Self { Self { baz } }<|> 222 fn $0new(baz: String) -> Self { Self { baz } }
225} 223}
226", 224",
227 ); 225 );
@@ -231,7 +229,7 @@ impl Foo {
231"struct Foo { baz: String, qux: Vec<i32> } 229"struct Foo { baz: String, qux: Vec<i32> }
232 230
233impl Foo { 231impl Foo {
234 fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|> 232 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
235} 233}
236", 234",
237 ); 235 );
@@ -243,7 +241,7 @@ impl Foo {
243"struct Foo { pub baz: String, pub qux: Vec<i32> } 241"struct Foo { pub baz: String, pub qux: Vec<i32> }
244 242
245impl Foo { 243impl Foo {
246 fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|> 244 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
247} 245}
248", 246",
249 ); 247 );
@@ -258,7 +256,7 @@ impl Foo {}
258"struct Foo {} 256"struct Foo {}
259 257
260impl Foo { 258impl Foo {
261 fn new() -> Self { Self { } }<|> 259 fn $0new() -> Self { Self { } }
262} 260}
263", 261",
264 ); 262 );
@@ -273,7 +271,7 @@ impl Foo {
273"struct Foo {} 271"struct Foo {}
274 272
275impl Foo { 273impl Foo {
276 fn new() -> Self { Self { } }<|> 274 fn $0new() -> Self { Self { } }
277 275
278 fn qux(&self) {} 276 fn qux(&self) {}
279} 277}
@@ -294,7 +292,7 @@ impl Foo {
294"struct Foo {} 292"struct Foo {}
295 293
296impl Foo { 294impl Foo {
297 fn new() -> Self { Self { } }<|> 295 fn $0new() -> Self { Self { } }
298 296
299 fn qux(&self) {} 297 fn qux(&self) {}
300 fn baz() -> i32 { 298 fn baz() -> i32 {
@@ -311,7 +309,7 @@ impl Foo {
311"pub struct Foo {} 309"pub struct Foo {}
312 310
313impl Foo { 311impl Foo {
314 pub fn new() -> Self { Self { } }<|> 312 pub fn $0new() -> Self { Self { } }
315} 313}
316", 314",
317 ); 315 );
@@ -321,7 +319,7 @@ impl Foo {
321"pub(crate) struct Foo {} 319"pub(crate) struct Foo {}
322 320
323impl Foo { 321impl Foo {
324 pub(crate) fn new() -> Self { Self { } }<|> 322 pub(crate) fn $0new() -> Self { Self { } }
325} 323}
326", 324",
327 ); 325 );
@@ -414,7 +412,7 @@ pub struct Source<T> {
414} 412}
415 413
416impl<T> Source<T> { 414impl<T> Source<T> {
417 pub fn new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }<|> 415 pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }
418 416
419 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { 417 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
420 Source { file_id: self.file_id, ast: f(self.ast) } 418 Source { file_id: self.file_id, ast: f(self.ast) }
diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs
new file mode 100644
index 000000000..26acf81f2
--- /dev/null
+++ b/crates/ra_assists/src/handlers/add_turbo_fish.rs
@@ -0,0 +1,134 @@
1use ra_ide_db::defs::{classify_name_ref, Definition, NameRefClass};
2use ra_syntax::{ast, AstNode, SyntaxKind, T};
3use test_utils::mark;
4
5use crate::{
6 assist_context::{AssistContext, Assists},
7 AssistId,
8};
9
10// Assist: add_turbo_fish
11//
12// Adds `::<_>` to a call of a generic method or function.
13//
14// ```
15// fn make<T>() -> T { todo!() }
16// fn main() {
17// let x = make<|>();
18// }
19// ```
20// ->
21// ```
22// fn make<T>() -> T { todo!() }
23// fn main() {
24// let x = make::<${0:_}>();
25// }
26// ```
27pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
28 let ident = ctx.find_token_at_offset(SyntaxKind::IDENT)?;
29 let next_token = ident.next_token()?;
30 if next_token.kind() == T![::] {
31 mark::hit!(add_turbo_fish_one_fish_is_enough);
32 return None;
33 }
34 let name_ref = ast::NameRef::cast(ident.parent())?;
35 let def = match classify_name_ref(&ctx.sema, &name_ref)? {
36 NameRefClass::Definition(def) => def,
37 NameRefClass::FieldShorthand { .. } => return None,
38 };
39 let fun = match def {
40 Definition::ModuleDef(hir::ModuleDef::Function(it)) => it,
41 _ => return None,
42 };
43 let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
44 if generics.is_empty() {
45 mark::hit!(add_turbo_fish_non_generic);
46 return None;
47 }
48 acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| {
49 match ctx.config.snippet_cap {
50 Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"),
51 None => builder.insert(ident.text_range().end(), "::<_>"),
52 }
53 })
54}
55
56#[cfg(test)]
57mod tests {
58 use crate::tests::{check_assist, check_assist_not_applicable};
59
60 use super::*;
61 use test_utils::mark;
62
63 #[test]
64 fn add_turbo_fish_function() {
65 check_assist(
66 add_turbo_fish,
67 r#"
68fn make<T>() -> T {}
69fn main() {
70 make<|>();
71}
72"#,
73 r#"
74fn make<T>() -> T {}
75fn main() {
76 make::<${0:_}>();
77}
78"#,
79 );
80 }
81
82 #[test]
83 fn add_turbo_fish_method() {
84 check_assist(
85 add_turbo_fish,
86 r#"
87struct S;
88impl S {
89 fn make<T>(&self) -> T {}
90}
91fn main() {
92 S.make<|>();
93}
94"#,
95 r#"
96struct S;
97impl S {
98 fn make<T>(&self) -> T {}
99}
100fn main() {
101 S.make::<${0:_}>();
102}
103"#,
104 );
105 }
106
107 #[test]
108 fn add_turbo_fish_one_fish_is_enough() {
109 mark::check!(add_turbo_fish_one_fish_is_enough);
110 check_assist_not_applicable(
111 add_turbo_fish,
112 r#"
113fn make<T>() -> T {}
114fn main() {
115 make<|>::<()>();
116}
117"#,
118 );
119 }
120
121 #[test]
122 fn add_turbo_fish_non_generic() {
123 mark::check!(add_turbo_fish_non_generic);
124 check_assist_not_applicable(
125 add_turbo_fish,
126 r#"
127fn make() -> () {}
128fn main() {
129 make<|>();
130}
131"#,
132 );
133 }
134}
diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs
index 0feba5e11..233e8fb8e 100644
--- a/crates/ra_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ra_assists/src/handlers/apply_demorgan.rs
@@ -63,22 +63,22 @@ mod tests {
63 63
64 #[test] 64 #[test]
65 fn demorgan_turns_and_into_or() { 65 fn demorgan_turns_and_into_or() {
66 check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x ||<|> x) }") 66 check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x || x) }")
67 } 67 }
68 68
69 #[test] 69 #[test]
70 fn demorgan_turns_or_into_and() { 70 fn demorgan_turns_or_into_and() {
71 check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x &&<|> x) }") 71 check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x && x) }")
72 } 72 }
73 73
74 #[test] 74 #[test]
75 fn demorgan_removes_inequality() { 75 fn demorgan_removes_inequality() {
76 check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x &&<|> x) }") 76 check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x && x) }")
77 } 77 }
78 78
79 #[test] 79 #[test]
80 fn demorgan_general_case() { 80 fn demorgan_general_case() {
81 check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x &&<|> !x) }") 81 check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x && !x) }")
82 } 82 }
83 83
84 #[test] 84 #[test]
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index 78d23150d..edf96d50e 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -50,7 +50,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
50 format!("Import `{}`", &import), 50 format!("Import `{}`", &import),
51 range, 51 range,
52 |builder| { 52 |builder| {
53 insert_use_statement(&auto_import_assets.syntax_under_caret, &import, ctx, builder); 53 insert_use_statement(
54 &auto_import_assets.syntax_under_caret,
55 &import,
56 ctx,
57 builder.text_edit_builder(),
58 );
54 }, 59 },
55 ); 60 );
56 } 61 }
@@ -293,7 +298,7 @@ mod tests {
293 } 298 }
294 ", 299 ",
295 r" 300 r"
296 <|>use PubMod::PubStruct; 301 use PubMod::PubStruct;
297 302
298 PubStruct 303 PubStruct
299 304
@@ -324,7 +329,7 @@ mod tests {
324 macro_rules! foo { 329 macro_rules! foo {
325 ($i:ident) => { fn foo(a: $i) {} } 330 ($i:ident) => { fn foo(a: $i) {} }
326 } 331 }
327 foo!(Pub<|>Struct); 332 foo!(PubStruct);
328 333
329 pub mod PubMod { 334 pub mod PubMod {
330 pub struct PubStruct; 335 pub struct PubStruct;
@@ -355,7 +360,7 @@ mod tests {
355 use PubMod::{PubStruct2, PubStruct1}; 360 use PubMod::{PubStruct2, PubStruct1};
356 361
357 struct Test { 362 struct Test {
358 test: Pub<|>Struct2<u8>, 363 test: PubStruct2<u8>,
359 } 364 }
360 365
361 pub mod PubMod { 366 pub mod PubMod {
@@ -388,7 +393,7 @@ mod tests {
388 r" 393 r"
389 use PubMod3::PubStruct; 394 use PubMod3::PubStruct;
390 395
391 PubSt<|>ruct 396 PubStruct
392 397
393 pub mod PubMod1 { 398 pub mod PubMod1 {
394 pub struct PubStruct; 399 pub struct PubStruct;
@@ -469,7 +474,7 @@ mod tests {
469 r" 474 r"
470 use PubMod::test_function; 475 use PubMod::test_function;
471 476
472 test_function<|> 477 test_function
473 478
474 pub mod PubMod { 479 pub mod PubMod {
475 pub fn test_function() {}; 480 pub fn test_function() {};
@@ -496,7 +501,7 @@ mod tests {
496 r"use crate_with_macro::foo; 501 r"use crate_with_macro::foo;
497 502
498fn main() { 503fn main() {
499 foo<|> 504 foo
500} 505}
501", 506",
502 ); 507 );
@@ -582,7 +587,7 @@ fn main() {
582 } 587 }
583 588
584 fn main() { 589 fn main() {
585 TestStruct::test_function<|> 590 TestStruct::test_function
586 } 591 }
587 ", 592 ",
588 ); 593 );
@@ -615,7 +620,7 @@ fn main() {
615 } 620 }
616 621
617 fn main() { 622 fn main() {
618 TestStruct::TEST_CONST<|> 623 TestStruct::TEST_CONST
619 } 624 }
620 ", 625 ",
621 ); 626 );
@@ -654,7 +659,7 @@ fn main() {
654 } 659 }
655 660
656 fn main() { 661 fn main() {
657 test_mod::TestStruct::test_function<|> 662 test_mod::TestStruct::test_function
658 } 663 }
659 ", 664 ",
660 ); 665 );
@@ -725,7 +730,7 @@ fn main() {
725 } 730 }
726 731
727 fn main() { 732 fn main() {
728 test_mod::TestStruct::TEST_CONST<|> 733 test_mod::TestStruct::TEST_CONST
729 } 734 }
730 ", 735 ",
731 ); 736 );
@@ -798,7 +803,7 @@ fn main() {
798 803
799 fn main() { 804 fn main() {
800 let test_struct = test_mod::TestStruct {}; 805 let test_struct = test_mod::TestStruct {};
801 test_struct.test_meth<|>od() 806 test_struct.test_method()
802 } 807 }
803 ", 808 ",
804 ); 809 );
diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs
index 5c907097e..c6baa0a57 100644
--- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs
+++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs
@@ -1,8 +1,6 @@
1use ra_syntax::{ 1use ra_syntax::{
2 ast::{self, BlockExpr, Expr, LoopBodyOwner}, 2 ast::{self, BlockExpr, Expr, LoopBodyOwner},
3 AstNode, 3 AstNode, SyntaxNode,
4 SyntaxKind::{COMMENT, WHITESPACE},
5 SyntaxNode, TextSize,
6}; 4};
7 5
8use crate::{AssistContext, AssistId, Assists}; 6use crate::{AssistContext, AssistId, Assists};
@@ -16,39 +14,40 @@ use crate::{AssistContext, AssistId, Assists};
16// ``` 14// ```
17// -> 15// ->
18// ``` 16// ```
19// fn foo() -> Result<i32, > { Ok(42i32) } 17// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
20// ``` 18// ```
21pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 19pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
22 let fn_def = ctx.find_node_at_offset::<ast::FnDef>(); 20 let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
23 let fn_def = &mut fn_def?; 21 // FIXME: extend to lambdas as well
24 let ret_type = &fn_def.ret_type()?.type_ref()?; 22 let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?;
25 if ret_type.syntax().text().to_string().starts_with("Result<") { 23
24 let type_ref = &ret_type.type_ref()?;
25 if type_ref.syntax().text().to_string().starts_with("Result<") {
26 return None; 26 return None;
27 } 27 }
28 28
29 let block_expr = &fn_def.body()?; 29 let block_expr = &fn_def.body()?;
30 let cursor_in_ret_type =
31 fn_def.ret_type()?.syntax().text_range().contains_range(ctx.frange.range);
32 if !cursor_in_ret_type {
33 return None;
34 }
35 30
36 acc.add( 31 acc.add(
37 AssistId("change_return_type_to_result"), 32 AssistId("change_return_type_to_result"),
38 "Change return type to Result", 33 "Change return type to Result",
39 ret_type.syntax().text_range(), 34 type_ref.syntax().text_range(),
40 |edit| { 35 |builder| {
41 let mut tail_return_expr_collector = TailReturnCollector::new(); 36 let mut tail_return_expr_collector = TailReturnCollector::new();
42 tail_return_expr_collector.collect_jump_exprs(block_expr, false); 37 tail_return_expr_collector.collect_jump_exprs(block_expr, false);
43 tail_return_expr_collector.collect_tail_exprs(block_expr); 38 tail_return_expr_collector.collect_tail_exprs(block_expr);
44 39
45 for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { 40 for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
46 edit.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg)); 41 builder.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg));
47 } 42 }
48 edit.replace_node_and_indent(ret_type.syntax(), format!("Result<{}, >", ret_type));
49 43
50 if let Some(node_start) = result_insertion_offset(&ret_type) { 44 match ctx.config.snippet_cap {
51 edit.set_cursor(node_start + TextSize::of(&format!("Result<{}, ", ret_type))); 45 Some(cap) => {
46 let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
47 builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
48 }
49 None => builder
50 .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
52 } 51 }
53 }, 52 },
54 ) 53 )
@@ -250,17 +249,8 @@ fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
250 } 249 }
251} 250}
252 251
253fn result_insertion_offset(ret_type: &ast::TypeRef) -> Option<TextSize> {
254 let non_ws_child = ret_type
255 .syntax()
256 .children_with_tokens()
257 .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
258 Some(non_ws_child.text_range().start())
259}
260
261#[cfg(test)] 252#[cfg(test)]
262mod tests { 253mod tests {
263
264 use crate::tests::{check_assist, check_assist_not_applicable}; 254 use crate::tests::{check_assist, check_assist_not_applicable};
265 255
266 use super::*; 256 use super::*;
@@ -273,7 +263,7 @@ mod tests {
273 let test = "test"; 263 let test = "test";
274 return 42i32; 264 return 42i32;
275 }"#, 265 }"#,
276 r#"fn foo() -> Result<i32, <|>> { 266 r#"fn foo() -> Result<i32, ${0:_}> {
277 let test = "test"; 267 let test = "test";
278 return Ok(42i32); 268 return Ok(42i32);
279 }"#, 269 }"#,
@@ -288,7 +278,7 @@ mod tests {
288 let test = "test"; 278 let test = "test";
289 return 42i32; 279 return 42i32;
290 }"#, 280 }"#,
291 r#"fn foo() -> Result<i32, <|>> { 281 r#"fn foo() -> Result<i32, ${0:_}> {
292 let test = "test"; 282 let test = "test";
293 return Ok(42i32); 283 return Ok(42i32);
294 }"#, 284 }"#,
@@ -314,7 +304,7 @@ mod tests {
314 let test = "test"; 304 let test = "test";
315 return 42i32; 305 return 42i32;
316 }"#, 306 }"#,
317 r#"fn foo() -> Result<i32, <|>> { 307 r#"fn foo() -> Result<i32, ${0:_}> {
318 let test = "test"; 308 let test = "test";
319 return Ok(42i32); 309 return Ok(42i32);
320 }"#, 310 }"#,
@@ -329,7 +319,7 @@ mod tests {
329 let test = "test"; 319 let test = "test";
330 42i32 320 42i32
331 }"#, 321 }"#,
332 r#"fn foo() -> Result<i32, <|>> { 322 r#"fn foo() -> Result<i32, ${0:_}> {
333 let test = "test"; 323 let test = "test";
334 Ok(42i32) 324 Ok(42i32)
335 }"#, 325 }"#,
@@ -343,7 +333,7 @@ mod tests {
343 r#"fn foo() -> i32<|> { 333 r#"fn foo() -> i32<|> {
344 42i32 334 42i32
345 }"#, 335 }"#,
346 r#"fn foo() -> Result<i32, <|>> { 336 r#"fn foo() -> Result<i32, ${0:_}> {
347 Ok(42i32) 337 Ok(42i32)
348 }"#, 338 }"#,
349 ); 339 );
@@ -359,7 +349,7 @@ mod tests {
359 24i32 349 24i32
360 } 350 }
361 }"#, 351 }"#,
362 r#"fn foo() -> Result<i32, <|>> { 352 r#"fn foo() -> Result<i32, ${0:_}> {
363 if true { 353 if true {
364 Ok(42i32) 354 Ok(42i32)
365 } else { 355 } else {
@@ -384,7 +374,7 @@ mod tests {
384 24i32 374 24i32
385 } 375 }
386 }"#, 376 }"#,
387 r#"fn foo() -> Result<i32, <|>> { 377 r#"fn foo() -> Result<i32, ${0:_}> {
388 if true { 378 if true {
389 if false { 379 if false {
390 Ok(1) 380 Ok(1)
@@ -413,7 +403,7 @@ mod tests {
413 24i32.await 403 24i32.await
414 } 404 }
415 }"#, 405 }"#,
416 r#"async fn foo() -> Result<i32, <|>> { 406 r#"async fn foo() -> Result<i32, ${0:_}> {
417 if true { 407 if true {
418 if false { 408 if false {
419 Ok(1.await) 409 Ok(1.await)
@@ -434,7 +424,7 @@ mod tests {
434 r#"fn foo() -> [i32;<|> 3] { 424 r#"fn foo() -> [i32;<|> 3] {
435 [1, 2, 3] 425 [1, 2, 3]
436 }"#, 426 }"#,
437 r#"fn foo() -> Result<[i32; 3], <|>> { 427 r#"fn foo() -> Result<[i32; 3], ${0:_}> {
438 Ok([1, 2, 3]) 428 Ok([1, 2, 3])
439 }"#, 429 }"#,
440 ); 430 );
@@ -455,7 +445,7 @@ mod tests {
455 24 as i32 445 24 as i32
456 } 446 }
457 }"#, 447 }"#,
458 r#"fn foo() -> Result<i32, <|>> { 448 r#"fn foo() -> Result<i32, ${0:_}> {
459 if true { 449 if true {
460 if false { 450 if false {
461 Ok(1 as i32) 451 Ok(1 as i32)
@@ -480,7 +470,7 @@ mod tests {
480 _ => 24i32, 470 _ => 24i32,
481 } 471 }
482 }"#, 472 }"#,
483 r#"fn foo() -> Result<i32, <|>> { 473 r#"fn foo() -> Result<i32, ${0:_}> {
484 let my_var = 5; 474 let my_var = 5;
485 match my_var { 475 match my_var {
486 5 => Ok(42i32), 476 5 => Ok(42i32),
@@ -503,7 +493,7 @@ mod tests {
503 493
504 my_var 494 my_var
505 }"#, 495 }"#,
506 r#"fn foo() -> Result<i32, <|>> { 496 r#"fn foo() -> Result<i32, ${0:_}> {
507 let my_var = 5; 497 let my_var = 5;
508 loop { 498 loop {
509 println!("test"); 499 println!("test");
@@ -526,7 +516,7 @@ mod tests {
526 516
527 my_var 517 my_var
528 }"#, 518 }"#,
529 r#"fn foo() -> Result<i32, <|>> { 519 r#"fn foo() -> Result<i32, ${0:_}> {
530 let my_var = let x = loop { 520 let my_var = let x = loop {
531 break 1; 521 break 1;
532 }; 522 };
@@ -549,7 +539,7 @@ mod tests {
549 539
550 res 540 res
551 }"#, 541 }"#,
552 r#"fn foo() -> Result<i32, <|>> { 542 r#"fn foo() -> Result<i32, ${0:_}> {
553 let my_var = 5; 543 let my_var = 5;
554 let res = match my_var { 544 let res = match my_var {
555 5 => 42i32, 545 5 => 42i32,
@@ -572,7 +562,7 @@ mod tests {
572 562
573 res 563 res
574 }"#, 564 }"#,
575 r#"fn foo() -> Result<i32, <|>> { 565 r#"fn foo() -> Result<i32, ${0:_}> {
576 let my_var = 5; 566 let my_var = 5;
577 let res = if my_var == 5 { 567 let res = if my_var == 5 {
578 42i32 568 42i32
@@ -608,7 +598,7 @@ mod tests {
608 }, 598 },
609 } 599 }
610 }"#, 600 }"#,
611 r#"fn foo() -> Result<i32, <|>> { 601 r#"fn foo() -> Result<i32, ${0:_}> {
612 let my_var = 5; 602 let my_var = 5;
613 match my_var { 603 match my_var {
614 5 => { 604 5 => {
@@ -641,7 +631,7 @@ mod tests {
641 } 631 }
642 53i32 632 53i32
643 }"#, 633 }"#,
644 r#"fn foo() -> Result<i32, <|>> { 634 r#"fn foo() -> Result<i32, ${0:_}> {
645 let test = "test"; 635 let test = "test";
646 if test == "test" { 636 if test == "test" {
647 return Ok(24i32); 637 return Ok(24i32);
@@ -672,7 +662,7 @@ mod tests {
672 662
673 the_field 663 the_field
674 }"#, 664 }"#,
675 r#"fn foo(the_field: u32) -> Result<u32, <|>> { 665 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
676 let true_closure = || { 666 let true_closure = || {
677 return true; 667 return true;
678 }; 668 };
@@ -711,7 +701,7 @@ mod tests {
711 701
712 t.unwrap_or_else(|| the_field) 702 t.unwrap_or_else(|| the_field)
713 }"#, 703 }"#,
714 r#"fn foo(the_field: u32) -> Result<u32, <|>> { 704 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
715 let true_closure = || { 705 let true_closure = || {
716 return true; 706 return true;
717 }; 707 };
@@ -749,7 +739,7 @@ mod tests {
749 i += 1; 739 i += 1;
750 } 740 }
751 }"#, 741 }"#,
752 r#"fn foo() -> Result<i32, <|>> { 742 r#"fn foo() -> Result<i32, ${0:_}> {
753 let test = "test"; 743 let test = "test";
754 if test == "test" { 744 if test == "test" {
755 return Ok(24i32); 745 return Ok(24i32);
@@ -781,7 +771,7 @@ mod tests {
781 } 771 }
782 } 772 }
783 }"#, 773 }"#,
784 r#"fn foo() -> Result<i32, <|>> { 774 r#"fn foo() -> Result<i32, ${0:_}> {
785 let test = "test"; 775 let test = "test";
786 if test == "test" { 776 if test == "test" {
787 return Ok(24i32); 777 return Ok(24i32);
@@ -819,7 +809,7 @@ mod tests {
819 } 809 }
820 } 810 }
821 }"#, 811 }"#,
822 r#"fn foo() -> Result<i32, <|>> { 812 r#"fn foo() -> Result<i32, ${0:_}> {
823 let test = "test"; 813 let test = "test";
824 let other = 5; 814 let other = 5;
825 if test == "test" { 815 if test == "test" {
@@ -860,7 +850,7 @@ mod tests {
860 850
861 the_field 851 the_field
862 }"#, 852 }"#,
863 r#"fn foo(the_field: u32) -> Result<u32, <|>> { 853 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
864 if the_field < 5 { 854 if the_field < 5 {
865 let mut i = 0; 855 let mut i = 0;
866 loop { 856 loop {
@@ -894,7 +884,7 @@ mod tests {
894 884
895 the_field 885 the_field
896 }"#, 886 }"#,
897 r#"fn foo(the_field: u32) -> Result<u32, <|>> { 887 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
898 if the_field < 5 { 888 if the_field < 5 {
899 let mut i = 0; 889 let mut i = 0;
900 890
@@ -923,7 +913,7 @@ mod tests {
923 913
924 the_field 914 the_field
925 }"#, 915 }"#,
926 r#"fn foo(the_field: u32) -> Result<u32, <|>> { 916 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
927 if the_field < 5 { 917 if the_field < 5 {
928 let mut i = 0; 918 let mut i = 0;
929 919
@@ -953,7 +943,7 @@ mod tests {
953 943
954 the_field 944 the_field
955 }"#, 945 }"#,
956 r#"fn foo(the_field: u32) -> Result<u32, <|>> { 946 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
957 if the_field < 5 { 947 if the_field < 5 {
958 let mut i = 0; 948 let mut i = 0;
959 949
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index 40cf4b422..c21d75be0 100644
--- a/crates/ra_assists/src/handlers/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
@@ -5,14 +5,11 @@ use ra_syntax::{
5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY, 5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY,
6 WHITESPACE, 6 WHITESPACE,
7 }, 7 },
8 SyntaxNode, TextRange, TextSize, T, 8 SyntaxNode, TextSize, T,
9}; 9};
10 10use test_utils::mark;
11use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
12use test_utils::tested_by;
13 11
14use crate::{AssistContext, AssistId, Assists}; 12use crate::{AssistContext, AssistId, Assists};
15use ra_db::FileId;
16 13
17// Assist: change_visibility 14// Assist: change_visibility
18// 15//
@@ -30,8 +27,6 @@ pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Optio
30 return change_vis(acc, vis); 27 return change_vis(acc, vis);
31 } 28 }
32 add_vis(acc, ctx) 29 add_vis(acc, ctx)
33 .or_else(|| add_vis_to_referenced_module_def(acc, ctx))
34 .or_else(|| add_vis_to_referenced_record_field(acc, ctx))
35} 30}
36 31
37fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 32fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
@@ -55,7 +50,7 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
55 } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { 50 } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() {
56 let field = field_name.syntax().ancestors().find_map(ast::RecordFieldDef::cast)?; 51 let field = field_name.syntax().ancestors().find_map(ast::RecordFieldDef::cast)?;
57 if field.name()? != field_name { 52 if field.name()? != field_name {
58 tested_by!(change_visibility_field_false_positive); 53 mark::hit!(change_visibility_field_false_positive);
59 return None; 54 return None;
60 } 55 }
61 if field.visibility().is_some() { 56 if field.visibility().is_some() {
@@ -73,147 +68,9 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
73 68
74 acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| { 69 acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| {
75 edit.insert(offset, "pub(crate) "); 70 edit.insert(offset, "pub(crate) ");
76 edit.set_cursor(offset);
77 })
78}
79
80fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
81 let path: ast::Path = ctx.find_node_at_offset()?;
82 let path_res = ctx.sema.resolve_path(&path)?;
83 let def = match path_res {
84 PathResolution::Def(def) => def,
85 _ => return None,
86 };
87
88 let current_module = ctx.sema.scope(&path.syntax()).module()?;
89 let target_module = def.module(ctx.db)?;
90
91 let vis = target_module.visibility_of(ctx.db, &def)?;
92 if vis.is_visible_from(ctx.db, current_module.into()) {
93 return None;
94 };
95
96 let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?;
97
98 let missing_visibility =
99 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
100
101 let assist_label = match target_name {
102 None => format!("Change visibility to {}", missing_visibility),
103 Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
104 };
105
106 acc.add(AssistId("change_visibility"), assist_label, target, |edit| {
107 edit.set_file(target_file);
108 edit.insert(offset, format!("{} ", missing_visibility));
109 edit.set_cursor(offset);
110 })
111}
112
113fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
114 let record_field: ast::RecordField = ctx.find_node_at_offset()?;
115 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?;
116
117 let current_module = ctx.sema.scope(record_field.syntax()).module()?;
118 let visibility = record_field_def.visibility(ctx.db);
119 if visibility.is_visible_from(ctx.db, current_module.into()) {
120 return None;
121 }
122
123 let parent = record_field_def.parent_def(ctx.db);
124 let parent_name = parent.name(ctx.db);
125 let target_module = parent.module(ctx.db);
126
127 let in_file_source = record_field_def.source(ctx.db);
128 let (offset, target) = match in_file_source.value {
129 hir::FieldSource::Named(it) => {
130 let s = it.syntax();
131 (vis_offset(s), s.text_range())
132 }
133 hir::FieldSource::Pos(it) => {
134 let s = it.syntax();
135 (vis_offset(s), s.text_range())
136 }
137 };
138
139 let missing_visibility =
140 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
141 let target_file = in_file_source.file_id.original_file(ctx.db);
142
143 let target_name = record_field_def.name(ctx.db);
144 let assist_label =
145 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
146
147 acc.add(AssistId("change_visibility"), assist_label, target, |edit| {
148 edit.set_file(target_file);
149 edit.insert(offset, format!("{} ", missing_visibility));
150 edit.set_cursor(offset)
151 }) 71 })
152} 72}
153 73
154fn target_data_for_def(
155 db: &dyn HirDatabase,
156 def: hir::ModuleDef,
157) -> Option<(TextSize, TextRange, FileId, Option<hir::Name>)> {
158 fn offset_target_and_file_id<S, Ast>(
159 db: &dyn HirDatabase,
160 x: S,
161 ) -> (TextSize, TextRange, FileId)
162 where
163 S: HasSource<Ast = Ast>,
164 Ast: AstNode,
165 {
166 let source = x.source(db);
167 let in_file_syntax = source.syntax();
168 let file_id = in_file_syntax.file_id;
169 let syntax = in_file_syntax.value;
170 (vis_offset(syntax), syntax.text_range(), file_id.original_file(db.upcast()))
171 }
172
173 let target_name;
174 let (offset, target, target_file) = match def {
175 hir::ModuleDef::Function(f) => {
176 target_name = Some(f.name(db));
177 offset_target_and_file_id(db, f)
178 }
179 hir::ModuleDef::Adt(adt) => {
180 target_name = Some(adt.name(db));
181 match adt {
182 hir::Adt::Struct(s) => offset_target_and_file_id(db, s),
183 hir::Adt::Union(u) => offset_target_and_file_id(db, u),
184 hir::Adt::Enum(e) => offset_target_and_file_id(db, e),
185 }
186 }
187 hir::ModuleDef::Const(c) => {
188 target_name = c.name(db);
189 offset_target_and_file_id(db, c)
190 }
191 hir::ModuleDef::Static(s) => {
192 target_name = s.name(db);
193 offset_target_and_file_id(db, s)
194 }
195 hir::ModuleDef::Trait(t) => {
196 target_name = Some(t.name(db));
197 offset_target_and_file_id(db, t)
198 }
199 hir::ModuleDef::TypeAlias(t) => {
200 target_name = Some(t.name(db));
201 offset_target_and_file_id(db, t)
202 }
203 hir::ModuleDef::Module(m) => {
204 target_name = m.name(db);
205 let in_file_source = m.declaration_source(db)?;
206 let file_id = in_file_source.file_id.original_file(db.upcast());
207 let syntax = in_file_source.value.syntax();
208 (vis_offset(syntax), syntax.text_range(), file_id)
209 }
210 // Enum variants can't be private, we can't modify builtin types
211 hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None,
212 };
213
214 Some((offset, target, target_file, target_name))
215}
216
217fn vis_offset(node: &SyntaxNode) -> TextSize { 74fn vis_offset(node: &SyntaxNode) -> TextSize {
218 node.children_with_tokens() 75 node.children_with_tokens()
219 .skip_while(|it| match it.kind() { 76 .skip_while(|it| match it.kind() {
@@ -234,7 +91,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
234 target, 91 target,
235 |edit| { 92 |edit| {
236 edit.replace(vis.syntax().text_range(), "pub(crate)"); 93 edit.replace(vis.syntax().text_range(), "pub(crate)");
237 edit.set_cursor(vis.syntax().text_range().start())
238 }, 94 },
239 ); 95 );
240 } 96 }
@@ -246,7 +102,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
246 target, 102 target,
247 |edit| { 103 |edit| {
248 edit.replace(vis.syntax().text_range(), "pub"); 104 edit.replace(vis.syntax().text_range(), "pub");
249 edit.set_cursor(vis.syntax().text_range().start());
250 }, 105 },
251 ); 106 );
252 } 107 }
@@ -255,7 +110,7 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
255 110
256#[cfg(test)] 111#[cfg(test)]
257mod tests { 112mod tests {
258 use test_utils::covers; 113 use test_utils::mark;
259 114
260 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 115 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
261 116
@@ -263,17 +118,13 @@ mod tests {
263 118
264 #[test] 119 #[test]
265 fn change_visibility_adds_pub_crate_to_items() { 120 fn change_visibility_adds_pub_crate_to_items() {
266 check_assist(change_visibility, "<|>fn foo() {}", "<|>pub(crate) fn foo() {}"); 121 check_assist(change_visibility, "<|>fn foo() {}", "pub(crate) fn foo() {}");
267 check_assist(change_visibility, "f<|>n foo() {}", "<|>pub(crate) fn foo() {}"); 122 check_assist(change_visibility, "f<|>n foo() {}", "pub(crate) fn foo() {}");
268 check_assist(change_visibility, "<|>struct Foo {}", "<|>pub(crate) struct Foo {}"); 123 check_assist(change_visibility, "<|>struct Foo {}", "pub(crate) struct Foo {}");
269 check_assist(change_visibility, "<|>mod foo {}", "<|>pub(crate) mod foo {}"); 124 check_assist(change_visibility, "<|>mod foo {}", "pub(crate) mod foo {}");
270 check_assist(change_visibility, "<|>trait Foo {}", "<|>pub(crate) trait Foo {}"); 125 check_assist(change_visibility, "<|>trait Foo {}", "pub(crate) trait Foo {}");
271 check_assist(change_visibility, "m<|>od {}", "<|>pub(crate) mod {}"); 126 check_assist(change_visibility, "m<|>od {}", "pub(crate) mod {}");
272 check_assist( 127 check_assist(change_visibility, "unsafe f<|>n foo() {}", "pub(crate) unsafe fn foo() {}");
273 change_visibility,
274 "unsafe f<|>n foo() {}",
275 "<|>pub(crate) unsafe fn foo() {}",
276 );
277 } 128 }
278 129
279 #[test] 130 #[test]
@@ -281,14 +132,14 @@ mod tests {
281 check_assist( 132 check_assist(
282 change_visibility, 133 change_visibility,
283 r"struct S { <|>field: u32 }", 134 r"struct S { <|>field: u32 }",
284 r"struct S { <|>pub(crate) field: u32 }", 135 r"struct S { pub(crate) field: u32 }",
285 ); 136 );
286 check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( <|>pub(crate) u32 )"); 137 check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( pub(crate) u32 )");
287 } 138 }
288 139
289 #[test] 140 #[test]
290 fn change_visibility_field_false_positive() { 141 fn change_visibility_field_false_positive() {
291 covers!(change_visibility_field_false_positive); 142 mark::check!(change_visibility_field_false_positive);
292 check_assist_not_applicable( 143 check_assist_not_applicable(
293 change_visibility, 144 change_visibility,
294 r"struct S { field: [(); { let <|>x = ();}] }", 145 r"struct S { field: [(); { let <|>x = ();}] }",
@@ -297,17 +148,17 @@ mod tests {
297 148
298 #[test] 149 #[test]
299 fn change_visibility_pub_to_pub_crate() { 150 fn change_visibility_pub_to_pub_crate() {
300 check_assist(change_visibility, "<|>pub fn foo() {}", "<|>pub(crate) fn foo() {}") 151 check_assist(change_visibility, "<|>pub fn foo() {}", "pub(crate) fn foo() {}")
301 } 152 }
302 153
303 #[test] 154 #[test]
304 fn change_visibility_pub_crate_to_pub() { 155 fn change_visibility_pub_crate_to_pub() {
305 check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "<|>pub fn foo() {}") 156 check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "pub fn foo() {}")
306 } 157 }
307 158
308 #[test] 159 #[test]
309 fn change_visibility_const() { 160 fn change_visibility_const() {
310 check_assist(change_visibility, "<|>const FOO = 3u8;", "<|>pub(crate) const FOO = 3u8;"); 161 check_assist(change_visibility, "<|>const FOO = 3u8;", "pub(crate) const FOO = 3u8;");
311 } 162 }
312 163
313 #[test] 164 #[test]
@@ -328,199 +179,12 @@ mod tests {
328 // comments 179 // comments
329 180
330 #[derive(Debug)] 181 #[derive(Debug)]
331 <|>pub(crate) struct Foo; 182 pub(crate) struct Foo;
332 ", 183 ",
333 ) 184 )
334 } 185 }
335 186
336 #[test] 187 #[test]
337 fn change_visibility_of_fn_via_path() {
338 check_assist(
339 change_visibility,
340 r"mod foo { fn foo() {} }
341 fn main() { foo::foo<|>() } ",
342 r"mod foo { <|>pub(crate) fn foo() {} }
343 fn main() { foo::foo() } ",
344 );
345 check_assist_not_applicable(
346 change_visibility,
347 r"mod foo { pub fn foo() {} }
348 fn main() { foo::foo<|>() } ",
349 )
350 }
351
352 #[test]
353 fn change_visibility_of_adt_in_submodule_via_path() {
354 check_assist(
355 change_visibility,
356 r"mod foo { struct Foo; }
357 fn main() { foo::Foo<|> } ",
358 r"mod foo { <|>pub(crate) struct Foo; }
359 fn main() { foo::Foo } ",
360 );
361 check_assist_not_applicable(
362 change_visibility,
363 r"mod foo { pub struct Foo; }
364 fn main() { foo::Foo<|> } ",
365 );
366 check_assist(
367 change_visibility,
368 r"mod foo { enum Foo; }
369 fn main() { foo::Foo<|> } ",
370 r"mod foo { <|>pub(crate) enum Foo; }
371 fn main() { foo::Foo } ",
372 );
373 check_assist_not_applicable(
374 change_visibility,
375 r"mod foo { pub enum Foo; }
376 fn main() { foo::Foo<|> } ",
377 );
378 check_assist(
379 change_visibility,
380 r"mod foo { union Foo; }
381 fn main() { foo::Foo<|> } ",
382 r"mod foo { <|>pub(crate) union Foo; }
383 fn main() { foo::Foo } ",
384 );
385 check_assist_not_applicable(
386 change_visibility,
387 r"mod foo { pub union Foo; }
388 fn main() { foo::Foo<|> } ",
389 );
390 }
391
392 #[test]
393 fn change_visibility_of_adt_in_other_file_via_path() {
394 check_assist(
395 change_visibility,
396 r"
397 //- /main.rs
398 mod foo;
399 fn main() { foo::Foo<|> }
400
401 //- /foo.rs
402 struct Foo;
403 ",
404 r"<|>pub(crate) struct Foo;
405
406",
407 );
408 }
409
410 #[test]
411 fn change_visibility_of_struct_field_via_path() {
412 check_assist(
413 change_visibility,
414 r"mod foo { pub struct Foo { bar: (), } }
415 fn main() { foo::Foo { <|>bar: () }; } ",
416 r"mod foo { pub struct Foo { <|>pub(crate) bar: (), } }
417 fn main() { foo::Foo { bar: () }; } ",
418 );
419 check_assist(
420 change_visibility,
421 r"//- /lib.rs
422 mod foo;
423 fn main() { foo::Foo { <|>bar: () }; }
424 //- /foo.rs
425 pub struct Foo { bar: () }
426 ",
427 r"pub struct Foo { <|>pub(crate) bar: () }
428
429",
430 );
431 check_assist_not_applicable(
432 change_visibility,
433 r"mod foo { pub struct Foo { pub bar: (), } }
434 fn main() { foo::Foo { <|>bar: () }; } ",
435 );
436 check_assist_not_applicable(
437 change_visibility,
438 r"//- /lib.rs
439 mod foo;
440 fn main() { foo::Foo { <|>bar: () }; }
441 //- /foo.rs
442 pub struct Foo { pub bar: () }
443 ",
444 );
445 }
446
447 #[test]
448 fn change_visibility_of_enum_variant_field_via_path() {
449 check_assist(
450 change_visibility,
451 r"mod foo { pub enum Foo { Bar { bar: () } } }
452 fn main() { foo::Foo::Bar { <|>bar: () }; } ",
453 r"mod foo { pub enum Foo { Bar { <|>pub(crate) bar: () } } }
454 fn main() { foo::Foo::Bar { bar: () }; } ",
455 );
456 check_assist(
457 change_visibility,
458 r"//- /lib.rs
459 mod foo;
460 fn main() { foo::Foo::Bar { <|>bar: () }; }
461 //- /foo.rs
462 pub enum Foo { Bar { bar: () } }
463 ",
464 r"pub enum Foo { Bar { <|>pub(crate) bar: () } }
465
466",
467 );
468 check_assist_not_applicable(
469 change_visibility,
470 r"mod foo { pub struct Foo { pub bar: (), } }
471 fn main() { foo::Foo { <|>bar: () }; } ",
472 );
473 check_assist_not_applicable(
474 change_visibility,
475 r"//- /lib.rs
476 mod foo;
477 fn main() { foo::Foo { <|>bar: () }; }
478 //- /foo.rs
479 pub struct Foo { pub bar: () }
480 ",
481 );
482 }
483
484 #[test]
485 #[ignore]
486 // FIXME reenable this test when `Semantics::resolve_record_field` works with union fields
487 fn change_visibility_of_union_field_via_path() {
488 check_assist(
489 change_visibility,
490 r"mod foo { pub union Foo { bar: (), } }
491 fn main() { foo::Foo { <|>bar: () }; } ",
492 r"mod foo { pub union Foo { <|>pub(crate) bar: (), } }
493 fn main() { foo::Foo { bar: () }; } ",
494 );
495 check_assist(
496 change_visibility,
497 r"//- /lib.rs
498 mod foo;
499 fn main() { foo::Foo { <|>bar: () }; }
500 //- /foo.rs
501 pub union Foo { bar: () }
502 ",
503 r"pub union Foo { <|>pub(crate) bar: () }
504
505",
506 );
507 check_assist_not_applicable(
508 change_visibility,
509 r"mod foo { pub union Foo { pub bar: (), } }
510 fn main() { foo::Foo { <|>bar: () }; } ",
511 );
512 check_assist_not_applicable(
513 change_visibility,
514 r"//- /lib.rs
515 mod foo;
516 fn main() { foo::Foo { <|>bar: () }; }
517 //- /foo.rs
518 pub union Foo { pub bar: () }
519 ",
520 );
521 }
522
523 #[test]
524 fn not_applicable_for_enum_variants() { 188 fn not_applicable_for_enum_variants() {
525 check_assist_not_applicable( 189 check_assist_not_applicable(
526 change_visibility, 190 change_visibility,
@@ -530,182 +194,6 @@ mod tests {
530 } 194 }
531 195
532 #[test] 196 #[test]
533 fn change_visibility_of_const_via_path() {
534 check_assist(
535 change_visibility,
536 r"mod foo { const FOO: () = (); }
537 fn main() { foo::FOO<|> } ",
538 r"mod foo { <|>pub(crate) const FOO: () = (); }
539 fn main() { foo::FOO } ",
540 );
541 check_assist_not_applicable(
542 change_visibility,
543 r"mod foo { pub const FOO: () = (); }
544 fn main() { foo::FOO<|> } ",
545 );
546 }
547
548 #[test]
549 fn change_visibility_of_static_via_path() {
550 check_assist(
551 change_visibility,
552 r"mod foo { static FOO: () = (); }
553 fn main() { foo::FOO<|> } ",
554 r"mod foo { <|>pub(crate) static FOO: () = (); }
555 fn main() { foo::FOO } ",
556 );
557 check_assist_not_applicable(
558 change_visibility,
559 r"mod foo { pub static FOO: () = (); }
560 fn main() { foo::FOO<|> } ",
561 );
562 }
563
564 #[test]
565 fn change_visibility_of_trait_via_path() {
566 check_assist(
567 change_visibility,
568 r"mod foo { trait Foo { fn foo(&self) {} } }
569 fn main() { let x: &dyn foo::<|>Foo; } ",
570 r"mod foo { <|>pub(crate) trait Foo { fn foo(&self) {} } }
571 fn main() { let x: &dyn foo::Foo; } ",
572 );
573 check_assist_not_applicable(
574 change_visibility,
575 r"mod foo { pub trait Foo { fn foo(&self) {} } }
576 fn main() { let x: &dyn foo::Foo<|>; } ",
577 );
578 }
579
580 #[test]
581 fn change_visibility_of_type_alias_via_path() {
582 check_assist(
583 change_visibility,
584 r"mod foo { type Foo = (); }
585 fn main() { let x: foo::Foo<|>; } ",
586 r"mod foo { <|>pub(crate) type Foo = (); }
587 fn main() { let x: foo::Foo; } ",
588 );
589 check_assist_not_applicable(
590 change_visibility,
591 r"mod foo { pub type Foo = (); }
592 fn main() { let x: foo::Foo<|>; } ",
593 );
594 }
595
596 #[test]
597 fn change_visibility_of_module_via_path() {
598 check_assist(
599 change_visibility,
600 r"mod foo { mod bar { fn bar() {} } }
601 fn main() { foo::bar<|>::bar(); } ",
602 r"mod foo { <|>pub(crate) mod bar { fn bar() {} } }
603 fn main() { foo::bar::bar(); } ",
604 );
605
606 check_assist(
607 change_visibility,
608 r"
609 //- /main.rs
610 mod foo;
611 fn main() { foo::bar<|>::baz(); }
612
613 //- /foo.rs
614 mod bar {
615 pub fn baz() {}
616 }
617 ",
618 r"<|>pub(crate) mod bar {
619 pub fn baz() {}
620}
621
622",
623 );
624
625 check_assist_not_applicable(
626 change_visibility,
627 r"mod foo { pub mod bar { pub fn bar() {} } }
628 fn main() { foo::bar<|>::bar(); } ",
629 );
630 }
631
632 #[test]
633 fn change_visibility_of_inline_module_in_other_file_via_path() {
634 check_assist(
635 change_visibility,
636 r"
637 //- /main.rs
638 mod foo;
639 fn main() { foo::bar<|>::baz(); }
640
641 //- /foo.rs
642 mod bar;
643
644 //- /foo/bar.rs
645 pub fn baz() {}
646 }
647 ",
648 r"<|>pub(crate) mod bar;
649",
650 );
651 }
652
653 #[test]
654 fn change_visibility_of_module_declaration_in_other_file_via_path() {
655 check_assist(
656 change_visibility,
657 r"//- /main.rs
658 mod foo;
659 fn main() { foo::bar<|>>::baz(); }
660
661 //- /foo.rs
662 mod bar {
663 pub fn baz() {}
664 }",
665 r"<|>pub(crate) mod bar {
666 pub fn baz() {}
667}
668",
669 );
670 }
671
672 #[test]
673 #[ignore]
674 // FIXME handle reexports properly
675 fn change_visibility_of_reexport() {
676 check_assist(
677 change_visibility,
678 r"
679 mod foo {
680 use bar::Baz;
681 mod bar { pub(super) struct Baz; }
682 }
683 foo::Baz<|>
684 ",
685 r"
686 mod foo {
687 <|>pub(crate) use bar::Baz;
688 mod bar { pub(super) struct Baz; }
689 }
690 foo::Baz
691 ",
692 )
693 }
694
695 #[test]
696 fn adds_pub_when_target_is_in_another_crate() {
697 check_assist(
698 change_visibility,
699 r"//- /main.rs crate:a deps:foo
700 foo::Bar<|>
701 //- /lib.rs crate:foo
702 struct Bar;",
703 r"<|>pub struct Bar;
704",
705 )
706 }
707
708 #[test]
709 fn change_visibility_target() { 197 fn change_visibility_target() {
710 check_assist_target(change_visibility, "<|>fn foo() {}", "fn"); 198 check_assist_target(change_visibility, "<|>fn foo() {}", "fn");
711 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)"); 199 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)");
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs
index 66b296081..4cc75a7ce 100644
--- a/crates/ra_assists/src/handlers/early_return.rs
+++ b/crates/ra_assists/src/handlers/early_return.rs
@@ -97,7 +97,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
97 } 97 }
98 98
99 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; 99 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
100 let cursor_position = ctx.offset();
101 100
102 let target = if_expr.syntax().text_range(); 101 let target = if_expr.syntax().text_range();
103 acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { 102 acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| {
@@ -148,7 +147,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
148 } 147 }
149 }; 148 };
150 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); 149 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
151 edit.set_cursor(cursor_position);
152 150
153 fn replace( 151 fn replace(
154 new_expr: &SyntaxNode, 152 new_expr: &SyntaxNode,
@@ -207,7 +205,7 @@ mod tests {
207 r#" 205 r#"
208 fn main() { 206 fn main() {
209 bar(); 207 bar();
210 if<|> !true { 208 if !true {
211 return; 209 return;
212 } 210 }
213 foo(); 211 foo();
@@ -237,7 +235,7 @@ mod tests {
237 r#" 235 r#"
238 fn main(n: Option<String>) { 236 fn main(n: Option<String>) {
239 bar(); 237 bar();
240 le<|>t n = match n { 238 let n = match n {
241 Some(it) => it, 239 Some(it) => it,
242 _ => return, 240 _ => return,
243 }; 241 };
@@ -263,7 +261,7 @@ mod tests {
263 "#, 261 "#,
264 r#" 262 r#"
265 fn main() { 263 fn main() {
266 le<|>t x = match Err(92) { 264 let x = match Err(92) {
267 Ok(it) => it, 265 Ok(it) => it,
268 _ => return, 266 _ => return,
269 }; 267 };
@@ -291,7 +289,7 @@ mod tests {
291 r#" 289 r#"
292 fn main(n: Option<String>) { 290 fn main(n: Option<String>) {
293 bar(); 291 bar();
294 le<|>t n = match n { 292 let n = match n {
295 Ok(it) => it, 293 Ok(it) => it,
296 _ => return, 294 _ => return,
297 }; 295 };
@@ -321,7 +319,7 @@ mod tests {
321 r#" 319 r#"
322 fn main() { 320 fn main() {
323 while true { 321 while true {
324 if<|> !true { 322 if !true {
325 continue; 323 continue;
326 } 324 }
327 foo(); 325 foo();
@@ -349,7 +347,7 @@ mod tests {
349 r#" 347 r#"
350 fn main() { 348 fn main() {
351 while true { 349 while true {
352 le<|>t n = match n { 350 let n = match n {
353 Some(it) => it, 351 Some(it) => it,
354 _ => continue, 352 _ => continue,
355 }; 353 };
@@ -378,7 +376,7 @@ mod tests {
378 r#" 376 r#"
379 fn main() { 377 fn main() {
380 loop { 378 loop {
381 if<|> !true { 379 if !true {
382 continue; 380 continue;
383 } 381 }
384 foo(); 382 foo();
@@ -406,7 +404,7 @@ mod tests {
406 r#" 404 r#"
407 fn main() { 405 fn main() {
408 loop { 406 loop {
409 le<|>t n = match n { 407 let n = match n {
410 Some(it) => it, 408 Some(it) => it,
411 _ => continue, 409 _ => continue,
412 }; 410 };
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index 13c1e7e80..cc303285b 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -4,8 +4,12 @@ use hir::{Adt, HasSource, ModuleDef, Semantics};
4use itertools::Itertools; 4use itertools::Itertools;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; 6use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
7use test_utils::mark;
7 8
8use crate::{AssistContext, AssistId, Assists}; 9use crate::{
10 utils::{render_snippet, Cursor, FamousDefs},
11 AssistContext, AssistId, Assists,
12};
9 13
10// Assist: fill_match_arms 14// Assist: fill_match_arms
11// 15//
@@ -26,7 +30,7 @@ use crate::{AssistContext, AssistId, Assists};
26// 30//
27// fn handle(action: Action) { 31// fn handle(action: Action) {
28// match action { 32// match action {
29// Action::Move { distance } => {} 33// $0Action::Move { distance } => {}
30// Action::Stop => {} 34// Action::Stop => {}
31// } 35// }
32// } 36// }
@@ -49,12 +53,18 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
49 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { 53 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
50 let variants = enum_def.variants(ctx.db); 54 let variants = enum_def.variants(ctx.db);
51 55
52 variants 56 let mut variants = variants
53 .into_iter() 57 .into_iter()
54 .filter_map(|variant| build_pat(ctx.db, module, variant)) 58 .filter_map(|variant| build_pat(ctx.db, module, variant))
55 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
56 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
57 .collect() 61 .collect::<Vec<_>>();
62 if Some(enum_def) == FamousDefs(&ctx.sema, module.krate()).core_option_Option() {
63 // Match `Some` variant first.
64 mark::hit!(option_order);
65 variants.reverse()
66 }
67 variants
58 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { 68 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
59 // Partial fill not currently supported for tuple of enums. 69 // Partial fill not currently supported for tuple of enums.
60 if !arms.is_empty() { 70 if !arms.is_empty() {
@@ -93,10 +103,23 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
93 } 103 }
94 104
95 let target = match_expr.syntax().text_range(); 105 let target = match_expr.syntax().text_range();
96 acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |edit| { 106 acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |builder| {
97 let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms); 107 let new_arm_list = match_arm_list.remove_placeholder();
98 edit.set_cursor(expr.syntax().text_range().start()); 108 let n_old_arms = new_arm_list.arms().count();
99 edit.replace_ast(match_arm_list, new_arm_list); 109 let new_arm_list = new_arm_list.append_arms(missing_arms);
110 let first_new_arm = new_arm_list.arms().nth(n_old_arms);
111 let old_range = match_arm_list.syntax().text_range();
112 match (first_new_arm, ctx.config.snippet_cap) {
113 (Some(first_new_arm), Some(cap)) => {
114 let snippet = render_snippet(
115 cap,
116 new_arm_list.syntax(),
117 Cursor::Before(first_new_arm.syntax()),
118 );
119 builder.replace_snippet(cap, old_range, snippet);
120 }
121 _ => builder.replace(old_range, new_arm_list.to_string()),
122 }
100 }) 123 })
101} 124}
102 125
@@ -167,7 +190,12 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O
167 190
168#[cfg(test)] 191#[cfg(test)]
169mod tests { 192mod tests {
170 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 193 use test_utils::mark;
194
195 use crate::{
196 tests::{check_assist, check_assist_not_applicable, check_assist_target},
197 utils::FamousDefs,
198 };
171 199
172 use super::fill_match_arms; 200 use super::fill_match_arms;
173 201
@@ -214,12 +242,12 @@ mod tests {
214 r#" 242 r#"
215 enum A { 243 enum A {
216 As, 244 As,
217 Bs{x:i32, y:Option<i32>}, 245 Bs { x: i32, y: Option<i32> },
218 Cs(i32, Option<i32>), 246 Cs(i32, Option<i32>),
219 } 247 }
220 fn main() { 248 fn main() {
221 match A::As<|> { 249 match A::As<|> {
222 A::Bs{x,y:Some(_)} => {} 250 A::Bs { x, y: Some(_) } => {}
223 A::Cs(_, Some(_)) => {} 251 A::Cs(_, Some(_)) => {}
224 } 252 }
225 } 253 }
@@ -227,14 +255,14 @@ mod tests {
227 r#" 255 r#"
228 enum A { 256 enum A {
229 As, 257 As,
230 Bs{x:i32, y:Option<i32>}, 258 Bs { x: i32, y: Option<i32> },
231 Cs(i32, Option<i32>), 259 Cs(i32, Option<i32>),
232 } 260 }
233 fn main() { 261 fn main() {
234 match <|>A::As { 262 match A::As {
235 A::Bs{x,y:Some(_)} => {} 263 A::Bs { x, y: Some(_) } => {}
236 A::Cs(_, Some(_)) => {} 264 A::Cs(_, Some(_)) => {}
237 A::As => {} 265 $0A::As => {}
238 } 266 }
239 } 267 }
240 "#, 268 "#,
@@ -264,9 +292,9 @@ mod tests {
264 Cs(Option<i32>), 292 Cs(Option<i32>),
265 } 293 }
266 fn main() { 294 fn main() {
267 match <|>A::As { 295 match A::As {
268 A::Cs(_) | A::Bs => {} 296 A::Cs(_) | A::Bs => {}
269 A::As => {} 297 $0A::As => {}
270 } 298 }
271 } 299 }
272 "#, 300 "#,
@@ -310,11 +338,11 @@ mod tests {
310 Ys, 338 Ys,
311 } 339 }
312 fn main() { 340 fn main() {
313 match <|>A::As { 341 match A::As {
314 A::Bs if 0 < 1 => {} 342 A::Bs if 0 < 1 => {}
315 A::Ds(_value) => { let x = 1; } 343 A::Ds(_value) => { let x = 1; }
316 A::Es(B::Xs) => (), 344 A::Es(B::Xs) => (),
317 A::As => {} 345 $0A::As => {}
318 A::Cs => {} 346 A::Cs => {}
319 } 347 }
320 } 348 }
@@ -332,7 +360,7 @@ mod tests {
332 Bs, 360 Bs,
333 Cs(String), 361 Cs(String),
334 Ds(String, String), 362 Ds(String, String),
335 Es{ x: usize, y: usize } 363 Es { x: usize, y: usize }
336 } 364 }
337 365
338 fn main() { 366 fn main() {
@@ -346,13 +374,13 @@ mod tests {
346 Bs, 374 Bs,
347 Cs(String), 375 Cs(String),
348 Ds(String, String), 376 Ds(String, String),
349 Es{ x: usize, y: usize } 377 Es { x: usize, y: usize }
350 } 378 }
351 379
352 fn main() { 380 fn main() {
353 let a = A::As; 381 let a = A::As;
354 match <|>a { 382 match a {
355 A::As => {} 383 $0A::As => {}
356 A::Bs => {} 384 A::Bs => {}
357 A::Cs(_) => {} 385 A::Cs(_) => {}
358 A::Ds(_, _) => {} 386 A::Ds(_, _) => {}
@@ -368,14 +396,8 @@ mod tests {
368 check_assist( 396 check_assist(
369 fill_match_arms, 397 fill_match_arms,
370 r#" 398 r#"
371 enum A { 399 enum A { One, Two }
372 One, 400 enum B { One, Two }
373 Two,
374 }
375 enum B {
376 One,
377 Two,
378 }
379 401
380 fn main() { 402 fn main() {
381 let a = A::One; 403 let a = A::One;
@@ -384,20 +406,14 @@ mod tests {
384 } 406 }
385 "#, 407 "#,
386 r#" 408 r#"
387 enum A { 409 enum A { One, Two }
388 One, 410 enum B { One, Two }
389 Two,
390 }
391 enum B {
392 One,
393 Two,
394 }
395 411
396 fn main() { 412 fn main() {
397 let a = A::One; 413 let a = A::One;
398 let b = B::One; 414 let b = B::One;
399 match <|>(a, b) { 415 match (a, b) {
400 (A::One, B::One) => {} 416 $0(A::One, B::One) => {}
401 (A::One, B::Two) => {} 417 (A::One, B::Two) => {}
402 (A::Two, B::One) => {} 418 (A::Two, B::One) => {}
403 (A::Two, B::Two) => {} 419 (A::Two, B::Two) => {}
@@ -412,14 +428,8 @@ mod tests {
412 check_assist( 428 check_assist(
413 fill_match_arms, 429 fill_match_arms,
414 r#" 430 r#"
415 enum A { 431 enum A { One, Two }
416 One, 432 enum B { One, Two }
417 Two,
418 }
419 enum B {
420 One,
421 Two,
422 }
423 433
424 fn main() { 434 fn main() {
425 let a = A::One; 435 let a = A::One;
@@ -428,20 +438,14 @@ mod tests {
428 } 438 }
429 "#, 439 "#,
430 r#" 440 r#"
431 enum A { 441 enum A { One, Two }
432 One, 442 enum B { One, Two }
433 Two,
434 }
435 enum B {
436 One,
437 Two,
438 }
439 443
440 fn main() { 444 fn main() {
441 let a = A::One; 445 let a = A::One;
442 let b = B::One; 446 let b = B::One;
443 match <|>(&a, &b) { 447 match (&a, &b) {
444 (A::One, B::One) => {} 448 $0(A::One, B::One) => {}
445 (A::One, B::Two) => {} 449 (A::One, B::Two) => {}
446 (A::Two, B::One) => {} 450 (A::Two, B::One) => {}
447 (A::Two, B::Two) => {} 451 (A::Two, B::Two) => {}
@@ -456,14 +460,8 @@ mod tests {
456 check_assist_not_applicable( 460 check_assist_not_applicable(
457 fill_match_arms, 461 fill_match_arms,
458 r#" 462 r#"
459 enum A { 463 enum A { One, Two }
460 One, 464 enum B { One, Two }
461 Two,
462 }
463 enum B {
464 One,
465 Two,
466 }
467 465
468 fn main() { 466 fn main() {
469 let a = A::One; 467 let a = A::One;
@@ -481,14 +479,8 @@ mod tests {
481 check_assist_not_applicable( 479 check_assist_not_applicable(
482 fill_match_arms, 480 fill_match_arms,
483 r#" 481 r#"
484 enum A { 482 enum A { One, Two }
485 One, 483 enum B { One, Two }
486 Two,
487 }
488 enum B {
489 One,
490 Two,
491 }
492 484
493 fn main() { 485 fn main() {
494 let a = A::One; 486 let a = A::One;
@@ -512,10 +504,7 @@ mod tests {
512 check_assist_not_applicable( 504 check_assist_not_applicable(
513 fill_match_arms, 505 fill_match_arms,
514 r#" 506 r#"
515 enum A { 507 enum A { One, Two }
516 One,
517 Two,
518 }
519 508
520 fn main() { 509 fn main() {
521 let a = A::One; 510 let a = A::One;
@@ -531,9 +520,7 @@ mod tests {
531 check_assist( 520 check_assist(
532 fill_match_arms, 521 fill_match_arms,
533 r#" 522 r#"
534 enum A { 523 enum A { As }
535 As,
536 }
537 524
538 fn foo(a: &A) { 525 fn foo(a: &A) {
539 match a<|> { 526 match a<|> {
@@ -541,13 +528,11 @@ mod tests {
541 } 528 }
542 "#, 529 "#,
543 r#" 530 r#"
544 enum A { 531 enum A { As }
545 As,
546 }
547 532
548 fn foo(a: &A) { 533 fn foo(a: &A) {
549 match <|>a { 534 match a {
550 A::As => {} 535 $0A::As => {}
551 } 536 }
552 } 537 }
553 "#, 538 "#,
@@ -557,7 +542,7 @@ mod tests {
557 fill_match_arms, 542 fill_match_arms,
558 r#" 543 r#"
559 enum A { 544 enum A {
560 Es{ x: usize, y: usize } 545 Es { x: usize, y: usize }
561 } 546 }
562 547
563 fn foo(a: &mut A) { 548 fn foo(a: &mut A) {
@@ -567,12 +552,12 @@ mod tests {
567 "#, 552 "#,
568 r#" 553 r#"
569 enum A { 554 enum A {
570 Es{ x: usize, y: usize } 555 Es { x: usize, y: usize }
571 } 556 }
572 557
573 fn foo(a: &mut A) { 558 fn foo(a: &mut A) {
574 match <|>a { 559 match a {
575 A::Es { x, y } => {} 560 $0A::Es { x, y } => {}
576 } 561 }
577 } 562 }
578 "#, 563 "#,
@@ -611,8 +596,8 @@ mod tests {
611 enum E { X, Y } 596 enum E { X, Y }
612 597
613 fn main() { 598 fn main() {
614 match <|>E::X { 599 match E::X {
615 E::X => {} 600 $0E::X => {}
616 E::Y => {} 601 E::Y => {}
617 } 602 }
618 } 603 }
@@ -639,8 +624,8 @@ mod tests {
639 use foo::E::X; 624 use foo::E::X;
640 625
641 fn main() { 626 fn main() {
642 match <|>X { 627 match X {
643 X => {} 628 $0X => {}
644 foo::E::Y => {} 629 foo::E::Y => {}
645 } 630 }
646 } 631 }
@@ -653,10 +638,7 @@ mod tests {
653 check_assist( 638 check_assist(
654 fill_match_arms, 639 fill_match_arms,
655 r#" 640 r#"
656 enum A { 641 enum A { One, Two }
657 One,
658 Two,
659 }
660 fn foo(a: A) { 642 fn foo(a: A) {
661 match a { 643 match a {
662 // foo bar baz<|> 644 // foo bar baz<|>
@@ -666,16 +648,13 @@ mod tests {
666 } 648 }
667 "#, 649 "#,
668 r#" 650 r#"
669 enum A { 651 enum A { One, Two }
670 One,
671 Two,
672 }
673 fn foo(a: A) { 652 fn foo(a: A) {
674 match <|>a { 653 match a {
675 // foo bar baz 654 // foo bar baz
676 A::One => {} 655 A::One => {}
677 // This is where the rest should be 656 // This is where the rest should be
678 A::Two => {} 657 $0A::Two => {}
679 } 658 }
680 } 659 }
681 "#, 660 "#,
@@ -687,10 +666,7 @@ mod tests {
687 check_assist( 666 check_assist(
688 fill_match_arms, 667 fill_match_arms,
689 r#" 668 r#"
690 enum A { 669 enum A { One, Two }
691 One,
692 Two,
693 }
694 fn foo(a: A) { 670 fn foo(a: A) {
695 match a { 671 match a {
696 // foo bar baz<|> 672 // foo bar baz<|>
@@ -698,14 +674,11 @@ mod tests {
698 } 674 }
699 "#, 675 "#,
700 r#" 676 r#"
701 enum A { 677 enum A { One, Two }
702 One,
703 Two,
704 }
705 fn foo(a: A) { 678 fn foo(a: A) {
706 match <|>a { 679 match a {
707 // foo bar baz 680 // foo bar baz
708 A::One => {} 681 $0A::One => {}
709 A::Two => {} 682 A::Two => {}
710 } 683 }
711 } 684 }
@@ -728,12 +701,37 @@ mod tests {
728 r#" 701 r#"
729 enum A { One, Two, } 702 enum A { One, Two, }
730 fn foo(a: A) { 703 fn foo(a: A) {
731 match <|>a { 704 match a {
732 A::One => {} 705 $0A::One => {}
733 A::Two => {} 706 A::Two => {}
734 } 707 }
735 } 708 }
736 "#, 709 "#,
737 ); 710 );
738 } 711 }
712
713 #[test]
714 fn option_order() {
715 mark::check!(option_order);
716 let before = r#"
717fn foo(opt: Option<i32>) {
718 match opt<|> {
719 }
720}"#;
721 let before =
722 &format!("//- main.rs crate:main deps:core\n{}{}", before, FamousDefs::FIXTURE);
723
724 check_assist(
725 fill_match_arms,
726 before,
727 r#"
728fn foo(opt: Option<i32>) {
729 match opt {
730 $0Some(_) => {}
731 None => {}
732 }
733}
734"#,
735 );
736 }
739} 737}
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs
new file mode 100644
index 000000000..9ec42f568
--- /dev/null
+++ b/crates/ra_assists/src/handlers/fix_visibility.rs
@@ -0,0 +1,559 @@
1use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
2use ra_db::FileId;
3use ra_syntax::{
4 ast, AstNode,
5 SyntaxKind::{ATTR, COMMENT, WHITESPACE},
6 SyntaxNode, TextRange, TextSize,
7};
8
9use crate::{AssistContext, AssistId, Assists};
10
11// FIXME: this really should be a fix for diagnostic, rather than an assist.
12
13// Assist: fix_visibility
14//
15// Makes inaccessible item public.
16//
17// ```
18// mod m {
19// fn frobnicate() {}
20// }
21// fn main() {
22// m::frobnicate<|>() {}
23// }
24// ```
25// ->
26// ```
27// mod m {
28// $0pub(crate) fn frobnicate() {}
29// }
30// fn main() {
31// m::frobnicate() {}
32// }
33// ```
34pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
35 add_vis_to_referenced_module_def(acc, ctx)
36 .or_else(|| add_vis_to_referenced_record_field(acc, ctx))
37}
38
39fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
40 let path: ast::Path = ctx.find_node_at_offset()?;
41 let path_res = ctx.sema.resolve_path(&path)?;
42 let def = match path_res {
43 PathResolution::Def(def) => def,
44 _ => return None,
45 };
46
47 let current_module = ctx.sema.scope(&path.syntax()).module()?;
48 let target_module = def.module(ctx.db)?;
49
50 let vis = target_module.visibility_of(ctx.db, &def)?;
51 if vis.is_visible_from(ctx.db, current_module.into()) {
52 return None;
53 };
54
55 let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?;
56
57 let missing_visibility =
58 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
59
60 let assist_label = match target_name {
61 None => format!("Change visibility to {}", missing_visibility),
62 Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
63 };
64
65 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| {
66 builder.set_file(target_file);
67 match ctx.config.snippet_cap {
68 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
69 None => builder.insert(offset, format!("{} ", missing_visibility)),
70 }
71 })
72}
73
74fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
75 let record_field: ast::RecordField = ctx.find_node_at_offset()?;
76 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?;
77
78 let current_module = ctx.sema.scope(record_field.syntax()).module()?;
79 let visibility = record_field_def.visibility(ctx.db);
80 if visibility.is_visible_from(ctx.db, current_module.into()) {
81 return None;
82 }
83
84 let parent = record_field_def.parent_def(ctx.db);
85 let parent_name = parent.name(ctx.db);
86 let target_module = parent.module(ctx.db);
87
88 let in_file_source = record_field_def.source(ctx.db);
89 let (offset, target) = match in_file_source.value {
90 hir::FieldSource::Named(it) => {
91 let s = it.syntax();
92 (vis_offset(s), s.text_range())
93 }
94 hir::FieldSource::Pos(it) => {
95 let s = it.syntax();
96 (vis_offset(s), s.text_range())
97 }
98 };
99
100 let missing_visibility =
101 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
102 let target_file = in_file_source.file_id.original_file(ctx.db);
103
104 let target_name = record_field_def.name(ctx.db);
105 let assist_label =
106 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
107
108 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| {
109 builder.set_file(target_file);
110 match ctx.config.snippet_cap {
111 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
112 None => builder.insert(offset, format!("{} ", missing_visibility)),
113 }
114 })
115}
116
117fn target_data_for_def(
118 db: &dyn HirDatabase,
119 def: hir::ModuleDef,
120) -> Option<(TextSize, TextRange, FileId, Option<hir::Name>)> {
121 fn offset_target_and_file_id<S, Ast>(
122 db: &dyn HirDatabase,
123 x: S,
124 ) -> (TextSize, TextRange, FileId)
125 where
126 S: HasSource<Ast = Ast>,
127 Ast: AstNode,
128 {
129 let source = x.source(db);
130 let in_file_syntax = source.syntax();
131 let file_id = in_file_syntax.file_id;
132 let syntax = in_file_syntax.value;
133 (vis_offset(syntax), syntax.text_range(), file_id.original_file(db.upcast()))
134 }
135
136 let target_name;
137 let (offset, target, target_file) = match def {
138 hir::ModuleDef::Function(f) => {
139 target_name = Some(f.name(db));
140 offset_target_and_file_id(db, f)
141 }
142 hir::ModuleDef::Adt(adt) => {
143 target_name = Some(adt.name(db));
144 match adt {
145 hir::Adt::Struct(s) => offset_target_and_file_id(db, s),
146 hir::Adt::Union(u) => offset_target_and_file_id(db, u),
147 hir::Adt::Enum(e) => offset_target_and_file_id(db, e),
148 }
149 }
150 hir::ModuleDef::Const(c) => {
151 target_name = c.name(db);
152 offset_target_and_file_id(db, c)
153 }
154 hir::ModuleDef::Static(s) => {
155 target_name = s.name(db);
156 offset_target_and_file_id(db, s)
157 }
158 hir::ModuleDef::Trait(t) => {
159 target_name = Some(t.name(db));
160 offset_target_and_file_id(db, t)
161 }
162 hir::ModuleDef::TypeAlias(t) => {
163 target_name = Some(t.name(db));
164 offset_target_and_file_id(db, t)
165 }
166 hir::ModuleDef::Module(m) => {
167 target_name = m.name(db);
168 let in_file_source = m.declaration_source(db)?;
169 let file_id = in_file_source.file_id.original_file(db.upcast());
170 let syntax = in_file_source.value.syntax();
171 (vis_offset(syntax), syntax.text_range(), file_id)
172 }
173 // Enum variants can't be private, we can't modify builtin types
174 hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None,
175 };
176
177 Some((offset, target, target_file, target_name))
178}
179
180fn vis_offset(node: &SyntaxNode) -> TextSize {
181 node.children_with_tokens()
182 .skip_while(|it| match it.kind() {
183 WHITESPACE | COMMENT | ATTR => true,
184 _ => false,
185 })
186 .next()
187 .map(|it| it.text_range().start())
188 .unwrap_or_else(|| node.text_range().start())
189}
190
191#[cfg(test)]
192mod tests {
193 use crate::tests::{check_assist, check_assist_not_applicable};
194
195 use super::*;
196
197 #[test]
198 fn fix_visibility_of_fn() {
199 check_assist(
200 fix_visibility,
201 r"mod foo { fn foo() {} }
202 fn main() { foo::foo<|>() } ",
203 r"mod foo { $0pub(crate) fn foo() {} }
204 fn main() { foo::foo() } ",
205 );
206 check_assist_not_applicable(
207 fix_visibility,
208 r"mod foo { pub fn foo() {} }
209 fn main() { foo::foo<|>() } ",
210 )
211 }
212
213 #[test]
214 fn fix_visibility_of_adt_in_submodule() {
215 check_assist(
216 fix_visibility,
217 r"mod foo { struct Foo; }
218 fn main() { foo::Foo<|> } ",
219 r"mod foo { $0pub(crate) struct Foo; }
220 fn main() { foo::Foo } ",
221 );
222 check_assist_not_applicable(
223 fix_visibility,
224 r"mod foo { pub struct Foo; }
225 fn main() { foo::Foo<|> } ",
226 );
227 check_assist(
228 fix_visibility,
229 r"mod foo { enum Foo; }
230 fn main() { foo::Foo<|> } ",
231 r"mod foo { $0pub(crate) enum Foo; }
232 fn main() { foo::Foo } ",
233 );
234 check_assist_not_applicable(
235 fix_visibility,
236 r"mod foo { pub enum Foo; }
237 fn main() { foo::Foo<|> } ",
238 );
239 check_assist(
240 fix_visibility,
241 r"mod foo { union Foo; }
242 fn main() { foo::Foo<|> } ",
243 r"mod foo { $0pub(crate) union Foo; }
244 fn main() { foo::Foo } ",
245 );
246 check_assist_not_applicable(
247 fix_visibility,
248 r"mod foo { pub union Foo; }
249 fn main() { foo::Foo<|> } ",
250 );
251 }
252
253 #[test]
254 fn fix_visibility_of_adt_in_other_file() {
255 check_assist(
256 fix_visibility,
257 r"
258 //- /main.rs
259 mod foo;
260 fn main() { foo::Foo<|> }
261
262 //- /foo.rs
263 struct Foo;
264 ",
265 r"$0pub(crate) struct Foo;
266
267",
268 );
269 }
270
271 #[test]
272 fn fix_visibility_of_struct_field() {
273 check_assist(
274 fix_visibility,
275 r"mod foo { pub struct Foo { bar: (), } }
276 fn main() { foo::Foo { <|>bar: () }; } ",
277 r"mod foo { pub struct Foo { $0pub(crate) bar: (), } }
278 fn main() { foo::Foo { bar: () }; } ",
279 );
280 check_assist(
281 fix_visibility,
282 r"//- /lib.rs
283 mod foo;
284 fn main() { foo::Foo { <|>bar: () }; }
285 //- /foo.rs
286 pub struct Foo { bar: () }
287 ",
288 r"pub struct Foo { $0pub(crate) bar: () }
289
290",
291 );
292 check_assist_not_applicable(
293 fix_visibility,
294 r"mod foo { pub struct Foo { pub bar: (), } }
295 fn main() { foo::Foo { <|>bar: () }; } ",
296 );
297 check_assist_not_applicable(
298 fix_visibility,
299 r"//- /lib.rs
300 mod foo;
301 fn main() { foo::Foo { <|>bar: () }; }
302 //- /foo.rs
303 pub struct Foo { pub bar: () }
304 ",
305 );
306 }
307
308 #[test]
309 fn fix_visibility_of_enum_variant_field() {
310 check_assist(
311 fix_visibility,
312 r"mod foo { pub enum Foo { Bar { bar: () } } }
313 fn main() { foo::Foo::Bar { <|>bar: () }; } ",
314 r"mod foo { pub enum Foo { Bar { $0pub(crate) bar: () } } }
315 fn main() { foo::Foo::Bar { bar: () }; } ",
316 );
317 check_assist(
318 fix_visibility,
319 r"//- /lib.rs
320 mod foo;
321 fn main() { foo::Foo::Bar { <|>bar: () }; }
322 //- /foo.rs
323 pub enum Foo { Bar { bar: () } }
324 ",
325 r"pub enum Foo { Bar { $0pub(crate) bar: () } }
326
327",
328 );
329 check_assist_not_applicable(
330 fix_visibility,
331 r"mod foo { pub struct Foo { pub bar: (), } }
332 fn main() { foo::Foo { <|>bar: () }; } ",
333 );
334 check_assist_not_applicable(
335 fix_visibility,
336 r"//- /lib.rs
337 mod foo;
338 fn main() { foo::Foo { <|>bar: () }; }
339 //- /foo.rs
340 pub struct Foo { pub bar: () }
341 ",
342 );
343 }
344
345 #[test]
346 #[ignore]
347 // FIXME reenable this test when `Semantics::resolve_record_field` works with union fields
348 fn fix_visibility_of_union_field() {
349 check_assist(
350 fix_visibility,
351 r"mod foo { pub union Foo { bar: (), } }
352 fn main() { foo::Foo { <|>bar: () }; } ",
353 r"mod foo { pub union Foo { $0pub(crate) bar: (), } }
354 fn main() { foo::Foo { bar: () }; } ",
355 );
356 check_assist(
357 fix_visibility,
358 r"//- /lib.rs
359 mod foo;
360 fn main() { foo::Foo { <|>bar: () }; }
361 //- /foo.rs
362 pub union Foo { bar: () }
363 ",
364 r"pub union Foo { $0pub(crate) bar: () }
365
366",
367 );
368 check_assist_not_applicable(
369 fix_visibility,
370 r"mod foo { pub union Foo { pub bar: (), } }
371 fn main() { foo::Foo { <|>bar: () }; } ",
372 );
373 check_assist_not_applicable(
374 fix_visibility,
375 r"//- /lib.rs
376 mod foo;
377 fn main() { foo::Foo { <|>bar: () }; }
378 //- /foo.rs
379 pub union Foo { pub bar: () }
380 ",
381 );
382 }
383
384 #[test]
385 fn fix_visibility_of_const() {
386 check_assist(
387 fix_visibility,
388 r"mod foo { const FOO: () = (); }
389 fn main() { foo::FOO<|> } ",
390 r"mod foo { $0pub(crate) const FOO: () = (); }
391 fn main() { foo::FOO } ",
392 );
393 check_assist_not_applicable(
394 fix_visibility,
395 r"mod foo { pub const FOO: () = (); }
396 fn main() { foo::FOO<|> } ",
397 );
398 }
399
400 #[test]
401 fn fix_visibility_of_static() {
402 check_assist(
403 fix_visibility,
404 r"mod foo { static FOO: () = (); }
405 fn main() { foo::FOO<|> } ",
406 r"mod foo { $0pub(crate) static FOO: () = (); }
407 fn main() { foo::FOO } ",
408 );
409 check_assist_not_applicable(
410 fix_visibility,
411 r"mod foo { pub static FOO: () = (); }
412 fn main() { foo::FOO<|> } ",
413 );
414 }
415
416 #[test]
417 fn fix_visibility_of_trait() {
418 check_assist(
419 fix_visibility,
420 r"mod foo { trait Foo { fn foo(&self) {} } }
421 fn main() { let x: &dyn foo::<|>Foo; } ",
422 r"mod foo { $0pub(crate) trait Foo { fn foo(&self) {} } }
423 fn main() { let x: &dyn foo::Foo; } ",
424 );
425 check_assist_not_applicable(
426 fix_visibility,
427 r"mod foo { pub trait Foo { fn foo(&self) {} } }
428 fn main() { let x: &dyn foo::Foo<|>; } ",
429 );
430 }
431
432 #[test]
433 fn fix_visibility_of_type_alias() {
434 check_assist(
435 fix_visibility,
436 r"mod foo { type Foo = (); }
437 fn main() { let x: foo::Foo<|>; } ",
438 r"mod foo { $0pub(crate) type Foo = (); }
439 fn main() { let x: foo::Foo; } ",
440 );
441 check_assist_not_applicable(
442 fix_visibility,
443 r"mod foo { pub type Foo = (); }
444 fn main() { let x: foo::Foo<|>; } ",
445 );
446 }
447
448 #[test]
449 fn fix_visibility_of_module() {
450 check_assist(
451 fix_visibility,
452 r"mod foo { mod bar { fn bar() {} } }
453 fn main() { foo::bar<|>::bar(); } ",
454 r"mod foo { $0pub(crate) mod bar { fn bar() {} } }
455 fn main() { foo::bar::bar(); } ",
456 );
457
458 check_assist(
459 fix_visibility,
460 r"
461 //- /main.rs
462 mod foo;
463 fn main() { foo::bar<|>::baz(); }
464
465 //- /foo.rs
466 mod bar {
467 pub fn baz() {}
468 }
469 ",
470 r"$0pub(crate) mod bar {
471 pub fn baz() {}
472}
473
474",
475 );
476
477 check_assist_not_applicable(
478 fix_visibility,
479 r"mod foo { pub mod bar { pub fn bar() {} } }
480 fn main() { foo::bar<|>::bar(); } ",
481 );
482 }
483
484 #[test]
485 fn fix_visibility_of_inline_module_in_other_file() {
486 check_assist(
487 fix_visibility,
488 r"
489 //- /main.rs
490 mod foo;
491 fn main() { foo::bar<|>::baz(); }
492
493 //- /foo.rs
494 mod bar;
495
496 //- /foo/bar.rs
497 pub fn baz() {}
498 }
499 ",
500 r"$0pub(crate) mod bar;
501",
502 );
503 }
504
505 #[test]
506 fn fix_visibility_of_module_declaration_in_other_file() {
507 check_assist(
508 fix_visibility,
509 r"//- /main.rs
510 mod foo;
511 fn main() { foo::bar<|>>::baz(); }
512
513 //- /foo.rs
514 mod bar {
515 pub fn baz() {}
516 }",
517 r"$0pub(crate) mod bar {
518 pub fn baz() {}
519}
520",
521 );
522 }
523
524 #[test]
525 fn adds_pub_when_target_is_in_another_crate() {
526 check_assist(
527 fix_visibility,
528 r"//- /main.rs crate:a deps:foo
529 foo::Bar<|>
530 //- /lib.rs crate:foo
531 struct Bar;",
532 r"$0pub struct Bar;
533",
534 )
535 }
536
537 #[test]
538 #[ignore]
539 // FIXME handle reexports properly
540 fn fix_visibility_of_reexport() {
541 check_assist(
542 fix_visibility,
543 r"
544 mod foo {
545 use bar::Baz;
546 mod bar { pub(super) struct Baz; }
547 }
548 foo::Baz<|>
549 ",
550 r"
551 mod foo {
552 $0pub(crate) use bar::Baz;
553 mod bar { pub(super) struct Baz; }
554 }
555 foo::Baz
556 ",
557 )
558 }
559}
diff --git a/crates/ra_assists/src/handlers/flip_binexpr.rs b/crates/ra_assists/src/handlers/flip_binexpr.rs
index 692ba4895..573196576 100644
--- a/crates/ra_assists/src/handlers/flip_binexpr.rs
+++ b/crates/ra_assists/src/handlers/flip_binexpr.rs
@@ -85,17 +85,13 @@ mod tests {
85 check_assist( 85 check_assist(
86 flip_binexpr, 86 flip_binexpr,
87 "fn f() { let res = 1 ==<|> 2; }", 87 "fn f() { let res = 1 ==<|> 2; }",
88 "fn f() { let res = 2 ==<|> 1; }", 88 "fn f() { let res = 2 == 1; }",
89 ) 89 )
90 } 90 }
91 91
92 #[test] 92 #[test]
93 fn flip_binexpr_works_for_gt() { 93 fn flip_binexpr_works_for_gt() {
94 check_assist( 94 check_assist(flip_binexpr, "fn f() { let res = 1 ><|> 2; }", "fn f() { let res = 2 < 1; }")
95 flip_binexpr,
96 "fn f() { let res = 1 ><|> 2; }",
97 "fn f() { let res = 2 <<|> 1; }",
98 )
99 } 95 }
100 96
101 #[test] 97 #[test]
@@ -103,7 +99,7 @@ mod tests {
103 check_assist( 99 check_assist(
104 flip_binexpr, 100 flip_binexpr,
105 "fn f() { let res = 1 <=<|> 2; }", 101 "fn f() { let res = 1 <=<|> 2; }",
106 "fn f() { let res = 2 >=<|> 1; }", 102 "fn f() { let res = 2 >= 1; }",
107 ) 103 )
108 } 104 }
109 105
@@ -112,7 +108,7 @@ mod tests {
112 check_assist( 108 check_assist(
113 flip_binexpr, 109 flip_binexpr,
114 "fn f() { let res = (1 + 1) ==<|> (2 + 2); }", 110 "fn f() { let res = (1 + 1) ==<|> (2 + 2); }",
115 "fn f() { let res = (2 + 2) ==<|> (1 + 1); }", 111 "fn f() { let res = (2 + 2) == (1 + 1); }",
116 ) 112 )
117 } 113 }
118 114
@@ -132,7 +128,7 @@ mod tests {
132 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { 128 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
133 match other.downcast_ref::<Self>() { 129 match other.downcast_ref::<Self>() {
134 None => false, 130 None => false,
135 Some(it) => self ==<|> it, 131 Some(it) => self == it,
136 } 132 }
137 } 133 }
138 "#, 134 "#,
diff --git a/crates/ra_assists/src/handlers/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs
index dfe2a7fed..a57a1c463 100644
--- a/crates/ra_assists/src/handlers/flip_comma.rs
+++ b/crates/ra_assists/src/handlers/flip_comma.rs
@@ -45,7 +45,7 @@ mod tests {
45 check_assist( 45 check_assist(
46 flip_comma, 46 flip_comma,
47 "fn foo(x: i32,<|> y: Result<(), ()>) {}", 47 "fn foo(x: i32,<|> y: Result<(), ()>) {}",
48 "fn foo(y: Result<(), ()>,<|> x: i32) {}", 48 "fn foo(y: Result<(), ()>, x: i32) {}",
49 ) 49 )
50 } 50 }
51 51
diff --git a/crates/ra_assists/src/handlers/flip_trait_bound.rs b/crates/ra_assists/src/handlers/flip_trait_bound.rs
index 8a08702ab..0115adc8b 100644
--- a/crates/ra_assists/src/handlers/flip_trait_bound.rs
+++ b/crates/ra_assists/src/handlers/flip_trait_bound.rs
@@ -60,7 +60,7 @@ mod tests {
60 check_assist( 60 check_assist(
61 flip_trait_bound, 61 flip_trait_bound,
62 "struct S<T> where T: A <|>+ B { }", 62 "struct S<T> where T: A <|>+ B { }",
63 "struct S<T> where T: B <|>+ A { }", 63 "struct S<T> where T: B + A { }",
64 ) 64 )
65 } 65 }
66 66
@@ -69,13 +69,13 @@ mod tests {
69 check_assist( 69 check_assist(
70 flip_trait_bound, 70 flip_trait_bound,
71 "impl X for S<T> where T: A +<|> B { }", 71 "impl X for S<T> where T: A +<|> B { }",
72 "impl X for S<T> where T: B +<|> A { }", 72 "impl X for S<T> where T: B + A { }",
73 ) 73 )
74 } 74 }
75 75
76 #[test] 76 #[test]
77 fn flip_trait_bound_works_for_fn() { 77 fn flip_trait_bound_works_for_fn() {
78 check_assist(flip_trait_bound, "fn f<T: A <|>+ B>(t: T) { }", "fn f<T: B <|>+ A>(t: T) { }") 78 check_assist(flip_trait_bound, "fn f<T: A <|>+ B>(t: T) { }", "fn f<T: B + A>(t: T) { }")
79 } 79 }
80 80
81 #[test] 81 #[test]
@@ -83,7 +83,7 @@ mod tests {
83 check_assist( 83 check_assist(
84 flip_trait_bound, 84 flip_trait_bound,
85 "fn f<T>(t: T) where T: A +<|> B { }", 85 "fn f<T>(t: T) where T: A +<|> B { }",
86 "fn f<T>(t: T) where T: B +<|> A { }", 86 "fn f<T>(t: T) where T: B + A { }",
87 ) 87 )
88 } 88 }
89 89
@@ -92,7 +92,7 @@ mod tests {
92 check_assist( 92 check_assist(
93 flip_trait_bound, 93 flip_trait_bound,
94 "fn f<T>(t: T) where T: A <|>+ 'static { }", 94 "fn f<T>(t: T) where T: A <|>+ 'static { }",
95 "fn f<T>(t: T) where T: 'static <|>+ A { }", 95 "fn f<T>(t: T) where T: 'static + A { }",
96 ) 96 )
97 } 97 }
98 98
@@ -101,7 +101,7 @@ mod tests {
101 check_assist( 101 check_assist(
102 flip_trait_bound, 102 flip_trait_bound,
103 "struct S<T> where T: A<T> <|>+ b_mod::B<T> + C<T> { }", 103 "struct S<T> where T: A<T> <|>+ b_mod::B<T> + C<T> { }",
104 "struct S<T> where T: b_mod::B<T> <|>+ A<T> + C<T> { }", 104 "struct S<T> where T: b_mod::B<T> + A<T> + C<T> { }",
105 ) 105 )
106 } 106 }
107 107
@@ -110,7 +110,7 @@ mod tests {
110 check_assist( 110 check_assist(
111 flip_trait_bound, 111 flip_trait_bound,
112 "struct S<T> where T: A + B + C + D + E + F +<|> G + H + I + J { }", 112 "struct S<T> where T: A + B + C + D + E + F +<|> G + H + I + J { }",
113 "struct S<T> where T: A + B + C + D + E + G +<|> F + H + I + J { }", 113 "struct S<T> where T: A + B + C + D + E + G + F + H + I + J { }",
114 ) 114 )
115 } 115 }
116} 116}
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs
index 5b26814d3..d26e68847 100644
--- a/crates/ra_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ra_assists/src/handlers/inline_local_variable.rs
@@ -3,7 +3,7 @@ use ra_syntax::{
3 ast::{self, AstNode, AstToken}, 3 ast::{self, AstNode, AstToken},
4 TextRange, 4 TextRange,
5}; 5};
6use test_utils::tested_by; 6use test_utils::mark;
7 7
8use crate::{ 8use crate::{
9 assist_context::{AssistContext, Assists}, 9 assist_context::{AssistContext, Assists},
@@ -33,11 +33,11 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
33 _ => return None, 33 _ => return None,
34 }; 34 };
35 if bind_pat.mut_token().is_some() { 35 if bind_pat.mut_token().is_some() {
36 tested_by!(test_not_inline_mut_variable); 36 mark::hit!(test_not_inline_mut_variable);
37 return None; 37 return None;
38 } 38 }
39 if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { 39 if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) {
40 tested_by!(not_applicable_outside_of_bind_pat); 40 mark::hit!(not_applicable_outside_of_bind_pat);
41 return None; 41 return None;
42 } 42 }
43 let initializer_expr = let_stmt.initializer()?; 43 let initializer_expr = let_stmt.initializer()?;
@@ -46,7 +46,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
46 let def = Definition::Local(def); 46 let def = Definition::Local(def);
47 let refs = def.find_usages(ctx.db, None); 47 let refs = def.find_usages(ctx.db, None);
48 if refs.is_empty() { 48 if refs.is_empty() {
49 tested_by!(test_not_applicable_if_variable_unused); 49 mark::hit!(test_not_applicable_if_variable_unused);
50 return None; 50 return None;
51 }; 51 };
52 52
@@ -116,13 +116,12 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
116 let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() }; 116 let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() };
117 builder.replace(desc.file_range.range, replacement) 117 builder.replace(desc.file_range.range, replacement)
118 } 118 }
119 builder.set_cursor(delete_range.start())
120 }) 119 })
121} 120}
122 121
123#[cfg(test)] 122#[cfg(test)]
124mod tests { 123mod tests {
125 use test_utils::covers; 124 use test_utils::mark;
126 125
127 use crate::tests::{check_assist, check_assist_not_applicable}; 126 use crate::tests::{check_assist, check_assist_not_applicable};
128 127
@@ -149,7 +148,7 @@ fn foo() {
149 r" 148 r"
150fn bar(a: usize) {} 149fn bar(a: usize) {}
151fn foo() { 150fn foo() {
152 <|>1 + 1; 151 1 + 1;
153 if 1 > 10 { 152 if 1 > 10 {
154 } 153 }
155 154
@@ -183,7 +182,7 @@ fn foo() {
183 r" 182 r"
184fn bar(a: usize) {} 183fn bar(a: usize) {}
185fn foo() { 184fn foo() {
186 <|>(1 + 1) + 1; 185 (1 + 1) + 1;
187 if (1 + 1) > 10 { 186 if (1 + 1) > 10 {
188 } 187 }
189 188
@@ -217,7 +216,7 @@ fn foo() {
217 r" 216 r"
218fn bar(a: usize) {} 217fn bar(a: usize) {}
219fn foo() { 218fn foo() {
220 <|>bar(1) + 1; 219 bar(1) + 1;
221 if bar(1) > 10 { 220 if bar(1) > 10 {
222 } 221 }
223 222
@@ -251,7 +250,7 @@ fn foo() {
251 r" 250 r"
252fn bar(a: usize): usize { a } 251fn bar(a: usize): usize { a }
253fn foo() { 252fn foo() {
254 <|>(bar(1) as u64) + 1; 253 (bar(1) as u64) + 1;
255 if (bar(1) as u64) > 10 { 254 if (bar(1) as u64) > 10 {
256 } 255 }
257 256
@@ -283,7 +282,7 @@ fn foo() {
283}", 282}",
284 r" 283 r"
285fn foo() { 284fn foo() {
286 <|>{ 10 + 1 } + 1; 285 { 10 + 1 } + 1;
287 if { 10 + 1 } > 10 { 286 if { 10 + 1 } > 10 {
288 } 287 }
289 288
@@ -315,7 +314,7 @@ fn foo() {
315}", 314}",
316 r" 315 r"
317fn foo() { 316fn foo() {
318 <|>( 10 + 1 ) + 1; 317 ( 10 + 1 ) + 1;
319 if ( 10 + 1 ) > 10 { 318 if ( 10 + 1 ) > 10 {
320 } 319 }
321 320
@@ -330,7 +329,7 @@ fn foo() {
330 329
331 #[test] 330 #[test]
332 fn test_not_inline_mut_variable() { 331 fn test_not_inline_mut_variable() {
333 covers!(test_not_inline_mut_variable); 332 mark::check!(test_not_inline_mut_variable);
334 check_assist_not_applicable( 333 check_assist_not_applicable(
335 inline_local_variable, 334 inline_local_variable,
336 r" 335 r"
@@ -353,7 +352,7 @@ fn foo() {
353}", 352}",
354 r" 353 r"
355fn foo() { 354fn foo() {
356 <|>let b = bar(10 + 1) * 10; 355 let b = bar(10 + 1) * 10;
357 let c = bar(10 + 1) as usize; 356 let c = bar(10 + 1) as usize;
358}", 357}",
359 ); 358 );
@@ -373,7 +372,7 @@ fn foo() {
373 r" 372 r"
374fn foo() { 373fn foo() {
375 let x = vec![1, 2, 3]; 374 let x = vec![1, 2, 3];
376 <|>let b = x[0] * 10; 375 let b = x[0] * 10;
377 let c = x[0] as usize; 376 let c = x[0] as usize;
378}", 377}",
379 ); 378 );
@@ -393,7 +392,7 @@ fn foo() {
393 r" 392 r"
394fn foo() { 393fn foo() {
395 let bar = vec![1]; 394 let bar = vec![1];
396 <|>let b = bar.len() * 10; 395 let b = bar.len() * 10;
397 let c = bar.len() as usize; 396 let c = bar.len() as usize;
398}", 397}",
399 ); 398 );
@@ -421,7 +420,7 @@ struct Bar {
421 420
422fn foo() { 421fn foo() {
423 let bar = Bar { foo: 1 }; 422 let bar = Bar { foo: 1 };
424 <|>let b = bar.foo * 10; 423 let b = bar.foo * 10;
425 let c = bar.foo as usize; 424 let c = bar.foo as usize;
426}", 425}",
427 ); 426 );
@@ -442,7 +441,7 @@ fn foo() -> Option<usize> {
442 r" 441 r"
443fn foo() -> Option<usize> { 442fn foo() -> Option<usize> {
444 let bar = Some(1); 443 let bar = Some(1);
445 <|>let b = bar? * 10; 444 let b = bar? * 10;
446 let c = bar? as usize; 445 let c = bar? as usize;
447 None 446 None
448}", 447}",
@@ -462,7 +461,7 @@ fn foo() {
462 r" 461 r"
463fn foo() { 462fn foo() {
464 let bar = 10; 463 let bar = 10;
465 <|>let b = &bar * 10; 464 let b = &bar * 10;
466}", 465}",
467 ); 466 );
468 } 467 }
@@ -478,7 +477,7 @@ fn foo() {
478}", 477}",
479 r" 478 r"
480fn foo() { 479fn foo() {
481 <|>let b = (10, 20)[0]; 480 let b = (10, 20)[0];
482}", 481}",
483 ); 482 );
484 } 483 }
@@ -494,7 +493,7 @@ fn foo() {
494}", 493}",
495 r" 494 r"
496fn foo() { 495fn foo() {
497 <|>let b = [1, 2, 3].len(); 496 let b = [1, 2, 3].len();
498}", 497}",
499 ); 498 );
500 } 499 }
@@ -511,7 +510,7 @@ fn foo() {
511}", 510}",
512 r" 511 r"
513fn foo() { 512fn foo() {
514 <|>let b = (10 + 20) * 10; 513 let b = (10 + 20) * 10;
515 let c = (10 + 20) as usize; 514 let c = (10 + 20) as usize;
516}", 515}",
517 ); 516 );
@@ -531,7 +530,7 @@ fn foo() {
531 r" 530 r"
532fn foo() { 531fn foo() {
533 let d = 10; 532 let d = 10;
534 <|>let b = d * 10; 533 let b = d * 10;
535 let c = d as usize; 534 let c = d as usize;
536}", 535}",
537 ); 536 );
@@ -549,7 +548,7 @@ fn foo() {
549}", 548}",
550 r" 549 r"
551fn foo() { 550fn foo() {
552 <|>let b = { 10 } * 10; 551 let b = { 10 } * 10;
553 let c = { 10 } as usize; 552 let c = { 10 } as usize;
554}", 553}",
555 ); 554 );
@@ -569,7 +568,7 @@ fn foo() {
569}", 568}",
570 r" 569 r"
571fn foo() { 570fn foo() {
572 <|>let b = (10 + 20) * 10; 571 let b = (10 + 20) * 10;
573 let c = (10 + 20, 20); 572 let c = (10 + 20, 20);
574 let d = [10 + 20, 10]; 573 let d = [10 + 20, 10];
575 let e = (10 + 20); 574 let e = (10 + 20);
@@ -588,7 +587,7 @@ fn foo() {
588}", 587}",
589 r" 588 r"
590fn foo() { 589fn foo() {
591 <|>for i in vec![10, 20] {} 590 for i in vec![10, 20] {}
592}", 591}",
593 ); 592 );
594 } 593 }
@@ -604,7 +603,7 @@ fn foo() {
604}", 603}",
605 r" 604 r"
606fn foo() { 605fn foo() {
607 <|>while 1 > 0 {} 606 while 1 > 0 {}
608}", 607}",
609 ); 608 );
610 } 609 }
@@ -622,7 +621,7 @@ fn foo() {
622}", 621}",
623 r" 622 r"
624fn foo() { 623fn foo() {
625 <|>loop { 624 loop {
626 break 1 + 1; 625 break 1 + 1;
627 } 626 }
628}", 627}",
@@ -640,7 +639,7 @@ fn foo() {
640}", 639}",
641 r" 640 r"
642fn foo() { 641fn foo() {
643 <|>return 1 > 0; 642 return 1 > 0;
644}", 643}",
645 ); 644 );
646 } 645 }
@@ -656,14 +655,14 @@ fn foo() {
656}", 655}",
657 r" 656 r"
658fn foo() { 657fn foo() {
659 <|>match 1 > 0 {} 658 match 1 > 0 {}
660}", 659}",
661 ); 660 );
662 } 661 }
663 662
664 #[test] 663 #[test]
665 fn test_not_applicable_if_variable_unused() { 664 fn test_not_applicable_if_variable_unused() {
666 covers!(test_not_applicable_if_variable_unused); 665 mark::check!(test_not_applicable_if_variable_unused);
667 check_assist_not_applicable( 666 check_assist_not_applicable(
668 inline_local_variable, 667 inline_local_variable,
669 r" 668 r"
@@ -676,7 +675,7 @@ fn foo() {
676 675
677 #[test] 676 #[test]
678 fn not_applicable_outside_of_bind_pat() { 677 fn not_applicable_outside_of_bind_pat() {
679 covers!(not_applicable_outside_of_bind_pat); 678 mark::check!(not_applicable_outside_of_bind_pat);
680 check_assist_not_applicable( 679 check_assist_not_applicable(
681 inline_local_variable, 680 inline_local_variable,
682 r" 681 r"
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs
index fdf3ada0d..31d6539f7 100644
--- a/crates/ra_assists/src/handlers/introduce_variable.rs
+++ b/crates/ra_assists/src/handlers/introduce_variable.rs
@@ -4,10 +4,10 @@ use ra_syntax::{
4 BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR, 4 BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR,
5 WHITESPACE, 5 WHITESPACE,
6 }, 6 },
7 SyntaxNode, TextSize, 7 SyntaxNode,
8}; 8};
9use stdx::format_to; 9use stdx::format_to;
10use test_utils::tested_by; 10use test_utils::mark;
11 11
12use crate::{AssistContext, AssistId, Assists}; 12use crate::{AssistContext, AssistId, Assists};
13 13
@@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists};
23// -> 23// ->
24// ``` 24// ```
25// fn main() { 25// fn main() {
26// let var_name = (1 + 2); 26// let $0var_name = (1 + 2);
27// var_name * 4; 27// var_name * 4;
28// } 28// }
29// ``` 29// ```
@@ -33,7 +33,7 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti
33 } 33 }
34 let node = ctx.covering_element(); 34 let node = ctx.covering_element();
35 if node.kind() == COMMENT { 35 if node.kind() == COMMENT {
36 tested_by!(introduce_var_in_comment_is_not_applicable); 36 mark::hit!(introduce_var_in_comment_is_not_applicable);
37 return None; 37 return None;
38 } 38 }
39 let expr = node.ancestors().find_map(valid_target_expr)?; 39 let expr = node.ancestors().find_map(valid_target_expr)?;
@@ -46,14 +46,13 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti
46 acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { 46 acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| {
47 let mut buf = String::new(); 47 let mut buf = String::new();
48 48
49 let cursor_offset = if wrap_in_block { 49 if wrap_in_block {
50 buf.push_str("{ let var_name = "); 50 buf.push_str("{ let var_name = ");
51 TextSize::of("{ let ")
52 } else { 51 } else {
53 buf.push_str("let var_name = "); 52 buf.push_str("let var_name = ");
54 TextSize::of("let ")
55 }; 53 };
56 format_to!(buf, "{}", expr.syntax()); 54 format_to!(buf, "{}", expr.syntax());
55
57 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); 56 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
58 let is_full_stmt = if let Some(expr_stmt) = &full_stmt { 57 let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
59 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) 58 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
@@ -61,32 +60,47 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti
61 false 60 false
62 }; 61 };
63 if is_full_stmt { 62 if is_full_stmt {
64 tested_by!(test_introduce_var_expr_stmt); 63 mark::hit!(test_introduce_var_expr_stmt);
65 if full_stmt.unwrap().semicolon_token().is_none() { 64 if full_stmt.unwrap().semicolon_token().is_none() {
66 buf.push_str(";"); 65 buf.push_str(";");
67 } 66 }
68 edit.replace(expr.syntax().text_range(), buf); 67 let offset = expr.syntax().text_range();
69 } else { 68 match ctx.config.snippet_cap {
70 buf.push_str(";"); 69 Some(cap) => {
71 70 let snip = buf.replace("let var_name", "let $0var_name");
72 // We want to maintain the indent level, 71 edit.replace_snippet(cap, offset, snip)
73 // but we do not want to duplicate possible 72 }
74 // extra newlines in the indent block 73 None => edit.replace(offset, buf),
75 let text = indent.text();
76 if text.starts_with('\n') {
77 buf.push_str("\n");
78 buf.push_str(text.trim_start_matches('\n'));
79 } else {
80 buf.push_str(text);
81 } 74 }
75 return;
76 }
82 77
83 edit.replace(expr.syntax().text_range(), "var_name".to_string()); 78 buf.push_str(";");
84 edit.insert(anchor_stmt.text_range().start(), buf); 79
85 if wrap_in_block { 80 // We want to maintain the indent level,
86 edit.insert(anchor_stmt.text_range().end(), " }"); 81 // but we do not want to duplicate possible
82 // extra newlines in the indent block
83 let text = indent.text();
84 if text.starts_with('\n') {
85 buf.push_str("\n");
86 buf.push_str(text.trim_start_matches('\n'));
87 } else {
88 buf.push_str(text);
89 }
90
91 edit.replace(expr.syntax().text_range(), "var_name".to_string());
92 let offset = anchor_stmt.text_range().start();
93 match ctx.config.snippet_cap {
94 Some(cap) => {
95 let snip = buf.replace("let var_name", "let $0var_name");
96 edit.insert_snippet(cap, offset, snip)
87 } 97 }
98 None => edit.insert(offset, buf),
99 }
100
101 if wrap_in_block {
102 edit.insert(anchor_stmt.text_range().end(), " }");
88 } 103 }
89 edit.set_cursor(anchor_stmt.text_range().start() + cursor_offset);
90 }) 104 })
91} 105}
92 106
@@ -113,7 +127,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
113 expr.syntax().ancestors().find_map(|node| { 127 expr.syntax().ancestors().find_map(|node| {
114 if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) { 128 if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) {
115 if expr.syntax() == &node { 129 if expr.syntax() == &node {
116 tested_by!(test_introduce_var_last_expr); 130 mark::hit!(test_introduce_var_last_expr);
117 return Some((node, false)); 131 return Some((node, false));
118 } 132 }
119 } 133 }
@@ -134,7 +148,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
134 148
135#[cfg(test)] 149#[cfg(test)]
136mod tests { 150mod tests {
137 use test_utils::covers; 151 use test_utils::mark;
138 152
139 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 153 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
140 154
@@ -144,37 +158,37 @@ mod tests {
144 fn test_introduce_var_simple() { 158 fn test_introduce_var_simple() {
145 check_assist( 159 check_assist(
146 introduce_variable, 160 introduce_variable,
147 " 161 r#"
148fn foo() { 162fn foo() {
149 foo(<|>1 + 1<|>); 163 foo(<|>1 + 1<|>);
150}", 164}"#,
151 " 165 r#"
152fn foo() { 166fn foo() {
153 let <|>var_name = 1 + 1; 167 let $0var_name = 1 + 1;
154 foo(var_name); 168 foo(var_name);
155}", 169}"#,
156 ); 170 );
157 } 171 }
158 172
159 #[test] 173 #[test]
160 fn introduce_var_in_comment_is_not_applicable() { 174 fn introduce_var_in_comment_is_not_applicable() {
161 covers!(introduce_var_in_comment_is_not_applicable); 175 mark::check!(introduce_var_in_comment_is_not_applicable);
162 check_assist_not_applicable(introduce_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }"); 176 check_assist_not_applicable(introduce_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }");
163 } 177 }
164 178
165 #[test] 179 #[test]
166 fn test_introduce_var_expr_stmt() { 180 fn test_introduce_var_expr_stmt() {
167 covers!(test_introduce_var_expr_stmt); 181 mark::check!(test_introduce_var_expr_stmt);
168 check_assist( 182 check_assist(
169 introduce_variable, 183 introduce_variable,
170 " 184 r#"
171fn foo() { 185fn foo() {
172 <|>1 + 1<|>; 186 <|>1 + 1<|>;
173}", 187}"#,
174 " 188 r#"
175fn foo() { 189fn foo() {
176 let <|>var_name = 1 + 1; 190 let $0var_name = 1 + 1;
177}", 191}"#,
178 ); 192 );
179 check_assist( 193 check_assist(
180 introduce_variable, 194 introduce_variable,
@@ -185,7 +199,7 @@ fn foo() {
185}", 199}",
186 " 200 "
187fn foo() { 201fn foo() {
188 let <|>var_name = { let x = 0; x }; 202 let $0var_name = { let x = 0; x };
189 something_else(); 203 something_else();
190}", 204}",
191 ); 205 );
@@ -201,7 +215,7 @@ fn foo() {
201}", 215}",
202 " 216 "
203fn foo() { 217fn foo() {
204 let <|>var_name = 1; 218 let $0var_name = 1;
205 var_name + 1; 219 var_name + 1;
206}", 220}",
207 ); 221 );
@@ -209,7 +223,7 @@ fn foo() {
209 223
210 #[test] 224 #[test]
211 fn test_introduce_var_last_expr() { 225 fn test_introduce_var_last_expr() {
212 covers!(test_introduce_var_last_expr); 226 mark::check!(test_introduce_var_last_expr);
213 check_assist( 227 check_assist(
214 introduce_variable, 228 introduce_variable,
215 " 229 "
@@ -218,7 +232,7 @@ fn foo() {
218}", 232}",
219 " 233 "
220fn foo() { 234fn foo() {
221 let <|>var_name = 1 + 1; 235 let $0var_name = 1 + 1;
222 bar(var_name) 236 bar(var_name)
223}", 237}",
224 ); 238 );
@@ -230,7 +244,7 @@ fn foo() {
230}", 244}",
231 " 245 "
232fn foo() { 246fn foo() {
233 let <|>var_name = bar(1 + 1); 247 let $0var_name = bar(1 + 1);
234 var_name 248 var_name
235}", 249}",
236 ) 250 )
@@ -253,7 +267,7 @@ fn main() {
253fn main() { 267fn main() {
254 let x = true; 268 let x = true;
255 let tuple = match x { 269 let tuple = match x {
256 true => { let <|>var_name = 2 + 2; (var_name, true) } 270 true => { let $0var_name = 2 + 2; (var_name, true) }
257 _ => (0, false) 271 _ => (0, false)
258 }; 272 };
259} 273}
@@ -283,7 +297,7 @@ fn main() {
283 let tuple = match x { 297 let tuple = match x {
284 true => { 298 true => {
285 let y = 1; 299 let y = 1;
286 let <|>var_name = 2 + y; 300 let $0var_name = 2 + y;
287 (var_name, true) 301 (var_name, true)
288 } 302 }
289 _ => (0, false) 303 _ => (0, false)
@@ -304,7 +318,7 @@ fn main() {
304", 318",
305 " 319 "
306fn main() { 320fn main() {
307 let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; 321 let lambda = |x: u32| { let $0var_name = x * 2; var_name };
308} 322}
309", 323",
310 ); 324 );
@@ -321,7 +335,7 @@ fn main() {
321", 335",
322 " 336 "
323fn main() { 337fn main() {
324 let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; 338 let lambda = |x: u32| { let $0var_name = x * 2; var_name };
325} 339}
326", 340",
327 ); 341 );
@@ -338,7 +352,7 @@ fn main() {
338", 352",
339 " 353 "
340fn main() { 354fn main() {
341 let <|>var_name = Some(true); 355 let $0var_name = Some(true);
342 let o = var_name; 356 let o = var_name;
343} 357}
344", 358",
@@ -356,7 +370,7 @@ fn main() {
356", 370",
357 " 371 "
358fn main() { 372fn main() {
359 let <|>var_name = bar.foo(); 373 let $0var_name = bar.foo();
360 let v = var_name; 374 let v = var_name;
361} 375}
362", 376",
@@ -374,7 +388,7 @@ fn foo() -> u32 {
374", 388",
375 " 389 "
376fn foo() -> u32 { 390fn foo() -> u32 {
377 let <|>var_name = 2 + 2; 391 let $0var_name = 2 + 2;
378 return var_name; 392 return var_name;
379} 393}
380", 394",
@@ -396,7 +410,7 @@ fn foo() -> u32 {
396fn foo() -> u32 { 410fn foo() -> u32 {
397 411
398 412
399 let <|>var_name = 2 + 2; 413 let $0var_name = 2 + 2;
400 return var_name; 414 return var_name;
401} 415}
402", 416",
@@ -413,7 +427,7 @@ fn foo() -> u32 {
413 " 427 "
414fn foo() -> u32 { 428fn foo() -> u32 {
415 429
416 let <|>var_name = 2 + 2; 430 let $0var_name = 2 + 2;
417 return var_name; 431 return var_name;
418} 432}
419", 433",
@@ -438,7 +452,7 @@ fn foo() -> u32 {
438 // bar 452 // bar
439 453
440 454
441 let <|>var_name = 2 + 2; 455 let $0var_name = 2 + 2;
442 return var_name; 456 return var_name;
443} 457}
444", 458",
@@ -459,7 +473,7 @@ fn main() {
459 " 473 "
460fn main() { 474fn main() {
461 let result = loop { 475 let result = loop {
462 let <|>var_name = 2 + 2; 476 let $0var_name = 2 + 2;
463 break var_name; 477 break var_name;
464 }; 478 };
465} 479}
@@ -478,7 +492,7 @@ fn main() {
478", 492",
479 " 493 "
480fn main() { 494fn main() {
481 let <|>var_name = 0f32 as u32; 495 let $0var_name = 0f32 as u32;
482 let v = var_name; 496 let v = var_name;
483} 497}
484", 498",
diff --git a/crates/ra_assists/src/handlers/invert_if.rs b/crates/ra_assists/src/handlers/invert_if.rs
index 527c7caef..59d278eb9 100644
--- a/crates/ra_assists/src/handlers/invert_if.rs
+++ b/crates/ra_assists/src/handlers/invert_if.rs
@@ -72,7 +72,7 @@ mod tests {
72 check_assist( 72 check_assist(
73 invert_if, 73 invert_if,
74 "fn f() { i<|>f x != 3 { 1 } else { 3 + 2 } }", 74 "fn f() { i<|>f x != 3 { 1 } else { 3 + 2 } }",
75 "fn f() { i<|>f x == 3 { 3 + 2 } else { 1 } }", 75 "fn f() { if x == 3 { 3 + 2 } else { 1 } }",
76 ) 76 )
77 } 77 }
78 78
@@ -81,7 +81,7 @@ mod tests {
81 check_assist( 81 check_assist(
82 invert_if, 82 invert_if,
83 "fn f() { <|>if !cond { 3 * 2 } else { 1 } }", 83 "fn f() { <|>if !cond { 3 * 2 } else { 1 } }",
84 "fn f() { <|>if cond { 1 } else { 3 * 2 } }", 84 "fn f() { if cond { 1 } else { 3 * 2 } }",
85 ) 85 )
86 } 86 }
87 87
@@ -90,7 +90,7 @@ mod tests {
90 check_assist( 90 check_assist(
91 invert_if, 91 invert_if,
92 "fn f() { i<|>f cond { 3 * 2 } else { 1 } }", 92 "fn f() { i<|>f cond { 3 * 2 } else { 1 } }",
93 "fn f() { i<|>f !cond { 1 } else { 3 * 2 } }", 93 "fn f() { if !cond { 1 } else { 3 * 2 } }",
94 ) 94 )
95 } 95 }
96 96
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs
index ac3e53c27..972d16241 100644
--- a/crates/ra_assists/src/handlers/merge_imports.rs
+++ b/crates/ra_assists/src/handlers/merge_imports.rs
@@ -58,8 +58,6 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()
58 let target = tree.syntax().text_range(); 58 let target = tree.syntax().text_range();
59 acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| { 59 acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| {
60 builder.rewrite(rewriter); 60 builder.rewrite(rewriter);
61 // FIXME: we only need because our diff is imprecise
62 builder.set_cursor(offset);
63 }) 61 })
64} 62}
65 63
@@ -142,7 +140,7 @@ use std::fmt<|>::Debug;
142use std::fmt::Display; 140use std::fmt::Display;
143", 141",
144 r" 142 r"
145use std::fmt<|>::{Debug, Display}; 143use std::fmt::{Debug, Display};
146", 144",
147 ) 145 )
148 } 146 }
@@ -156,7 +154,7 @@ use std::fmt::Debug;
156use std::fmt<|>::Display; 154use std::fmt<|>::Display;
157", 155",
158 r" 156 r"
159use std::fmt:<|>:{Display, Debug}; 157use std::fmt::{Display, Debug};
160", 158",
161 ); 159 );
162 } 160 }
@@ -169,7 +167,7 @@ use std::fmt:<|>:{Display, Debug};
169use std::{fmt<|>::Debug, fmt::Display}; 167use std::{fmt<|>::Debug, fmt::Display};
170", 168",
171 r" 169 r"
172use std::{fmt<|>::{Debug, Display}}; 170use std::{fmt::{Debug, Display}};
173", 171",
174 ); 172 );
175 check_assist( 173 check_assist(
@@ -178,7 +176,7 @@ use std::{fmt<|>::{Debug, Display}};
178use std::{fmt::Debug, fmt<|>::Display}; 176use std::{fmt::Debug, fmt<|>::Display};
179", 177",
180 r" 178 r"
181use std::{fmt::<|>{Display, Debug}}; 179use std::{fmt::{Display, Debug}};
182", 180",
183 ); 181 );
184 } 182 }
@@ -192,7 +190,7 @@ use std<|>::cell::*;
192use std::str; 190use std::str;
193", 191",
194 r" 192 r"
195use std<|>::{cell::*, str}; 193use std::{cell::*, str};
196", 194",
197 ) 195 )
198 } 196 }
@@ -206,7 +204,7 @@ use std<|>::cell::*;
206use std::str::*; 204use std::str::*;
207", 205",
208 r" 206 r"
209use std<|>::{cell::*, str::*}; 207use std::{cell::*, str::*};
210", 208",
211 ) 209 )
212 } 210 }
@@ -222,7 +220,7 @@ use foo::baz;
222/// Doc comment 220/// Doc comment
223", 221",
224 r" 222 r"
225use foo<|>::{bar, baz}; 223use foo::{bar, baz};
226 224
227/// Doc comment 225/// Doc comment
228", 226",
@@ -241,7 +239,7 @@ use {
241", 239",
242 r" 240 r"
243use { 241use {
244 foo<|>::{bar, baz}, 242 foo::{bar, baz},
245}; 243};
246", 244",
247 ); 245 );
@@ -255,7 +253,7 @@ use {
255", 253",
256 r" 254 r"
257use { 255use {
258 foo::{bar<|>, baz}, 256 foo::{bar, baz},
259}; 257};
260", 258",
261 ); 259 );
@@ -272,7 +270,7 @@ use foo::<|>{
272}; 270};
273", 271",
274 r" 272 r"
275use foo::{<|> 273use foo::{
276 FooBar, 274 FooBar,
277bar::baz}; 275bar::baz};
278", 276",
diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs
index d4e38aa6a..ca04ec671 100644
--- a/crates/ra_assists/src/handlers/merge_match_arms.rs
+++ b/crates/ra_assists/src/handlers/merge_match_arms.rs
@@ -3,7 +3,7 @@ use std::iter::successors;
3use ra_syntax::{ 3use ra_syntax::{
4 algo::neighbor, 4 algo::neighbor,
5 ast::{self, AstNode}, 5 ast::{self, AstNode},
6 Direction, TextSize, 6 Direction,
7}; 7};
8 8
9use crate::{AssistContext, AssistId, Assists, TextRange}; 9use crate::{AssistContext, AssistId, Assists, TextRange};
@@ -41,17 +41,6 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option
41 let current_expr = current_arm.expr()?; 41 let current_expr = current_arm.expr()?;
42 let current_text_range = current_arm.syntax().text_range(); 42 let current_text_range = current_arm.syntax().text_range();
43 43
44 enum CursorPos {
45 InExpr(TextSize),
46 InPat(TextSize),
47 }
48 let cursor_pos = ctx.offset();
49 let cursor_pos = if current_expr.syntax().text_range().contains(cursor_pos) {
50 CursorPos::InExpr(current_text_range.end() - cursor_pos)
51 } else {
52 CursorPos::InPat(cursor_pos)
53 };
54
55 // We check if the following match arms match this one. We could, but don't, 44 // We check if the following match arms match this one. We could, but don't,
56 // compare to the previous match arm as well. 45 // compare to the previous match arm as well.
57 let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next)) 46 let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next))
@@ -87,10 +76,6 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option
87 let start = arms_to_merge.first().unwrap().syntax().text_range().start(); 76 let start = arms_to_merge.first().unwrap().syntax().text_range().start();
88 let end = arms_to_merge.last().unwrap().syntax().text_range().end(); 77 let end = arms_to_merge.last().unwrap().syntax().text_range().end();
89 78
90 edit.set_cursor(match cursor_pos {
91 CursorPos::InExpr(back_offset) => start + TextSize::of(&arm) - back_offset,
92 CursorPos::InPat(offset) => offset,
93 });
94 edit.replace(TextRange::new(start, end), arm); 79 edit.replace(TextRange::new(start, end), arm);
95 }) 80 })
96} 81}
@@ -132,7 +117,7 @@ mod tests {
132 fn main() { 117 fn main() {
133 let x = X::A; 118 let x = X::A;
134 let y = match x { 119 let y = match x {
135 X::A | X::B => { 1i32<|> } 120 X::A | X::B => { 1i32 }
136 X::C => { 2i32 } 121 X::C => { 2i32 }
137 } 122 }
138 } 123 }
@@ -164,7 +149,7 @@ mod tests {
164 fn main() { 149 fn main() {
165 let x = X::A; 150 let x = X::A;
166 let y = match x { 151 let y = match x {
167 X::A | X::B | X::C | X::D => {<|> 1i32 }, 152 X::A | X::B | X::C | X::D => { 1i32 },
168 X::E => { 2i32 }, 153 X::E => { 2i32 },
169 } 154 }
170 } 155 }
@@ -197,7 +182,7 @@ mod tests {
197 let x = X::A; 182 let x = X::A;
198 let y = match x { 183 let y = match x {
199 X::A => { 1i32 }, 184 X::A => { 1i32 },
200 _ => { 2i<|>32 } 185 _ => { 2i32 }
201 } 186 }
202 } 187 }
203 "#, 188 "#,
@@ -226,7 +211,7 @@ mod tests {
226 211
227 fn main() { 212 fn main() {
228 match X::A { 213 match X::A {
229 X::A<|> | X::B | X::C => 92, 214 X::A | X::B | X::C => 92,
230 X::D => 62, 215 X::D => 62,
231 _ => panic!(), 216 _ => panic!(),
232 } 217 }
diff --git a/crates/ra_assists/src/handlers/move_bounds.rs b/crates/ra_assists/src/handlers/move_bounds.rs
index a41aacfc3..be2a7eddc 100644
--- a/crates/ra_assists/src/handlers/move_bounds.rs
+++ b/crates/ra_assists/src/handlers/move_bounds.rs
@@ -99,7 +99,7 @@ mod tests {
99 fn foo<T: u32, <|>F: FnOnce(T) -> T>() {} 99 fn foo<T: u32, <|>F: FnOnce(T) -> T>() {}
100 "#, 100 "#,
101 r#" 101 r#"
102 fn foo<T, <|>F>() where T: u32, F: FnOnce(T) -> T {} 102 fn foo<T, F>() where T: u32, F: FnOnce(T) -> T {}
103 "#, 103 "#,
104 ); 104 );
105 } 105 }
@@ -112,7 +112,7 @@ mod tests {
112 impl<U: u32, <|>T> A<U, T> {} 112 impl<U: u32, <|>T> A<U, T> {}
113 "#, 113 "#,
114 r#" 114 r#"
115 impl<U, <|>T> A<U, T> where U: u32 {} 115 impl<U, T> A<U, T> where U: u32 {}
116 "#, 116 "#,
117 ); 117 );
118 } 118 }
@@ -125,7 +125,7 @@ mod tests {
125 struct A<<|>T: Iterator<Item = u32>> {} 125 struct A<<|>T: Iterator<Item = u32>> {}
126 "#, 126 "#,
127 r#" 127 r#"
128 struct A<<|>T> where T: Iterator<Item = u32> {} 128 struct A<T> where T: Iterator<Item = u32> {}
129 "#, 129 "#,
130 ); 130 );
131 } 131 }
@@ -138,7 +138,7 @@ mod tests {
138 struct Pair<<|>T: u32>(T, T); 138 struct Pair<<|>T: u32>(T, T);
139 "#, 139 "#,
140 r#" 140 r#"
141 struct Pair<<|>T>(T, T) where T: u32; 141 struct Pair<T>(T, T) where T: u32;
142 "#, 142 "#,
143 ); 143 );
144 } 144 }
diff --git a/crates/ra_assists/src/handlers/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs
index fc0335b57..7edcf0748 100644
--- a/crates/ra_assists/src/handlers/move_guard.rs
+++ b/crates/ra_assists/src/handlers/move_guard.rs
@@ -1,7 +1,6 @@
1use ra_syntax::{ 1use ra_syntax::{
2 ast, 2 ast::{AstNode, IfExpr, MatchArm},
3 ast::{AstNode, AstToken, IfExpr, MatchArm}, 3 SyntaxKind::WHITESPACE,
4 TextSize,
5}; 4};
6 5
7use crate::{AssistContext, AssistId, Assists}; 6use crate::{AssistContext, AssistId, Assists};
@@ -42,24 +41,15 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
42 41
43 let target = guard.syntax().text_range(); 42 let target = guard.syntax().text_range();
44 acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| { 43 acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| {
45 let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) { 44 match space_before_guard {
46 Some(tok) => { 45 Some(element) if element.kind() == WHITESPACE => {
47 if ast::Whitespace::cast(tok.clone()).is_some() { 46 edit.delete(element.text_range());
48 let ele = tok.text_range();
49 edit.delete(ele);
50 ele.len()
51 } else {
52 TextSize::from(0)
53 }
54 } 47 }
55 _ => TextSize::from(0), 48 _ => (),
56 }; 49 };
57 50
58 edit.delete(guard.syntax().text_range()); 51 edit.delete(guard.syntax().text_range());
59 edit.replace_node_and_indent(arm_expr.syntax(), buf); 52 edit.replace_node_and_indent(arm_expr.syntax(), buf);
60 edit.set_cursor(
61 arm_expr.syntax().text_range().start() + TextSize::from(3) - offseting_amount,
62 );
63 }) 53 })
64} 54}
65 55
@@ -124,7 +114,6 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
124 } 114 }
125 115
126 edit.insert(match_pat.syntax().text_range().end(), buf); 116 edit.insert(match_pat.syntax().text_range().end(), buf);
127 edit.set_cursor(match_pat.syntax().text_range().end() + TextSize::from(1));
128 }, 117 },
129 ) 118 )
130} 119}
@@ -172,7 +161,7 @@ mod tests {
172 let t = 'a'; 161 let t = 'a';
173 let chars = "abcd"; 162 let chars = "abcd";
174 match t { 163 match t {
175 '\r' => if chars.clone().next() == Some('\n') { <|>false }, 164 '\r' => if chars.clone().next() == Some('\n') { false },
176 _ => true 165 _ => true
177 } 166 }
178 } 167 }
@@ -195,7 +184,7 @@ mod tests {
195 r#" 184 r#"
196 fn f() { 185 fn f() {
197 match x { 186 match x {
198 y @ 4 | y @ 5 => if y > 5 { <|>true }, 187 y @ 4 | y @ 5 => if y > 5 { true },
199 _ => false 188 _ => false
200 } 189 }
201 } 190 }
@@ -222,7 +211,7 @@ mod tests {
222 let t = 'a'; 211 let t = 'a';
223 let chars = "abcd"; 212 let chars = "abcd";
224 match t { 213 match t {
225 '\r' <|>if chars.clone().next() == Some('\n') => false, 214 '\r' if chars.clone().next() == Some('\n') => false,
226 _ => true 215 _ => true
227 } 216 }
228 } 217 }
@@ -266,7 +255,7 @@ mod tests {
266 let t = 'a'; 255 let t = 'a';
267 let chars = "abcd"; 256 let chars = "abcd";
268 match t { 257 match t {
269 '\r' <|>if chars.clone().next().is_some() => { }, 258 '\r' if chars.clone().next().is_some() => { },
270 _ => true 259 _ => true
271 } 260 }
272 } 261 }
@@ -296,7 +285,7 @@ mod tests {
296 let mut t = 'a'; 285 let mut t = 'a';
297 let chars = "abcd"; 286 let chars = "abcd";
298 match t { 287 match t {
299 '\r' <|>if chars.clone().next().is_some() => { 288 '\r' if chars.clone().next().is_some() => {
300 t = 'e'; 289 t = 'e';
301 false 290 false
302 }, 291 },
diff --git a/crates/ra_assists/src/handlers/raw_string.rs b/crates/ra_assists/src/handlers/raw_string.rs
index c20ffe0b3..16002d2ac 100644
--- a/crates/ra_assists/src/handlers/raw_string.rs
+++ b/crates/ra_assists/src/handlers/raw_string.rs
@@ -164,7 +164,7 @@ mod test {
164 "#, 164 "#,
165 r##" 165 r##"
166 fn f() { 166 fn f() {
167 let s = <|>r#"random 167 let s = r#"random
168string"#; 168string"#;
169 } 169 }
170 "##, 170 "##,
@@ -182,7 +182,7 @@ string"#;
182 "#, 182 "#,
183 r##" 183 r##"
184 fn f() { 184 fn f() {
185 format!(<|>r#"x = {}"#, 92) 185 format!(r#"x = {}"#, 92)
186 } 186 }
187 "##, 187 "##,
188 ) 188 )
@@ -199,7 +199,7 @@ string"#;
199 "###, 199 "###,
200 r####" 200 r####"
201 fn f() { 201 fn f() {
202 let s = <|>r#"#random## 202 let s = r#"#random##
203string"#; 203string"#;
204 } 204 }
205 "####, 205 "####,
@@ -217,7 +217,7 @@ string"#;
217 "###, 217 "###,
218 r####" 218 r####"
219 fn f() { 219 fn f() {
220 let s = <|>r###"#random"## 220 let s = r###"#random"##
221string"###; 221string"###;
222 } 222 }
223 "####, 223 "####,
@@ -235,7 +235,7 @@ string"###;
235 "#, 235 "#,
236 r##" 236 r##"
237 fn f() { 237 fn f() {
238 let s = <|>r#"random string"#; 238 let s = r#"random string"#;
239 } 239 }
240 "##, 240 "##,
241 ) 241 )
@@ -289,7 +289,7 @@ string"###;
289 "#, 289 "#,
290 r##" 290 r##"
291 fn f() { 291 fn f() {
292 let s = <|>r#"random string"#; 292 let s = r#"random string"#;
293 } 293 }
294 "##, 294 "##,
295 ) 295 )
@@ -306,7 +306,7 @@ string"###;
306 "##, 306 "##,
307 r###" 307 r###"
308 fn f() { 308 fn f() {
309 let s = <|>r##"random"string"##; 309 let s = r##"random"string"##;
310 } 310 }
311 "###, 311 "###,
312 ) 312 )
@@ -348,7 +348,7 @@ string"###;
348 "##, 348 "##,
349 r#" 349 r#"
350 fn f() { 350 fn f() {
351 let s = <|>r"random string"; 351 let s = r"random string";
352 } 352 }
353 "#, 353 "#,
354 ) 354 )
@@ -365,7 +365,7 @@ string"###;
365 "##, 365 "##,
366 r#" 366 r#"
367 fn f() { 367 fn f() {
368 let s = <|>r"random\"str\"ing"; 368 let s = r"random\"str\"ing";
369 } 369 }
370 "#, 370 "#,
371 ) 371 )
@@ -382,7 +382,7 @@ string"###;
382 "###, 382 "###,
383 r##" 383 r##"
384 fn f() { 384 fn f() {
385 let s = <|>r#"random string"#; 385 let s = r#"random string"#;
386 } 386 }
387 "##, 387 "##,
388 ) 388 )
@@ -436,7 +436,7 @@ string"###;
436 "##, 436 "##,
437 r#" 437 r#"
438 fn f() { 438 fn f() {
439 let s = <|>"random string"; 439 let s = "random string";
440 } 440 }
441 "#, 441 "#,
442 ) 442 )
@@ -453,7 +453,7 @@ string"###;
453 "##, 453 "##,
454 r#" 454 r#"
455 fn f() { 455 fn f() {
456 let s = <|>"random\"str\"ing"; 456 let s = "random\"str\"ing";
457 } 457 }
458 "#, 458 "#,
459 ) 459 )
@@ -470,7 +470,7 @@ string"###;
470 "###, 470 "###,
471 r##" 471 r##"
472 fn f() { 472 fn f() {
473 let s = <|>"random string"; 473 let s = "random string";
474 } 474 }
475 "##, 475 "##,
476 ) 476 )
diff --git a/crates/ra_assists/src/handlers/remove_dbg.rs b/crates/ra_assists/src/handlers/remove_dbg.rs
index 8eef578cf..961ee1731 100644
--- a/crates/ra_assists/src/handlers/remove_dbg.rs
+++ b/crates/ra_assists/src/handlers/remove_dbg.rs
@@ -29,26 +29,6 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
29 29
30 let macro_range = macro_call.syntax().text_range(); 30 let macro_range = macro_call.syntax().text_range();
31 31
32 // If the cursor is inside the macro call, we'll try to maintain the cursor
33 // position by subtracting the length of dbg!( from the start of the file
34 // range, otherwise we'll default to using the start of the macro call
35 let cursor_pos = {
36 let file_range = ctx.frange.range;
37
38 let offset_start = file_range
39 .start()
40 .checked_sub(macro_range.start())
41 .unwrap_or_else(|| TextSize::from(0));
42
43 let dbg_size = TextSize::of("dbg!(");
44
45 if offset_start > dbg_size {
46 file_range.start() - dbg_size
47 } else {
48 macro_range.start()
49 }
50 };
51
52 let macro_content = { 32 let macro_content = {
53 let macro_args = macro_call.token_tree()?.syntax().clone(); 33 let macro_args = macro_call.token_tree()?.syntax().clone();
54 34
@@ -58,9 +38,8 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
58 }; 38 };
59 39
60 let target = macro_call.syntax().text_range(); 40 let target = macro_call.syntax().text_range();
61 acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |edit| { 41 acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |builder| {
62 edit.replace(macro_range, macro_content); 42 builder.replace(macro_range, macro_content);
63 edit.set_cursor(cursor_pos);
64 }) 43 })
65} 44}
66 45
@@ -94,13 +73,13 @@ mod tests {
94 73
95 #[test] 74 #[test]
96 fn test_remove_dbg() { 75 fn test_remove_dbg() {
97 check_assist(remove_dbg, "<|>dbg!(1 + 1)", "<|>1 + 1"); 76 check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");
98 77
99 check_assist(remove_dbg, "dbg!<|>((1 + 1))", "<|>(1 + 1)"); 78 check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)");
100 79
101 check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 <|>+ 1"); 80 check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1");
102 81
103 check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = <|>1 + 1"); 82 check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1");
104 83
105 check_assist( 84 check_assist(
106 remove_dbg, 85 remove_dbg,
@@ -113,7 +92,7 @@ fn foo(n: usize) {
113", 92",
114 " 93 "
115fn foo(n: usize) { 94fn foo(n: usize) {
116 if let Some(_) = n.<|>checked_sub(4) { 95 if let Some(_) = n.checked_sub(4) {
117 // ... 96 // ...
118 } 97 }
119} 98}
@@ -122,8 +101,8 @@ fn foo(n: usize) {
122 } 101 }
123 #[test] 102 #[test]
124 fn test_remove_dbg_with_brackets_and_braces() { 103 fn test_remove_dbg_with_brackets_and_braces() {
125 check_assist(remove_dbg, "dbg![<|>1 + 1]", "<|>1 + 1"); 104 check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1");
126 check_assist(remove_dbg, "dbg!{<|>1 + 1}", "<|>1 + 1"); 105 check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1");
127 } 106 }
128 107
129 #[test] 108 #[test]
diff --git a/crates/ra_assists/src/handlers/remove_mut.rs b/crates/ra_assists/src/handlers/remove_mut.rs
index dce546db7..fe4eada03 100644
--- a/crates/ra_assists/src/handlers/remove_mut.rs
+++ b/crates/ra_assists/src/handlers/remove_mut.rs
@@ -26,8 +26,7 @@ pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 }; 26 };
27 27
28 let target = mut_token.text_range(); 28 let target = mut_token.text_range();
29 acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| { 29 acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |builder| {
30 edit.set_cursor(delete_from); 30 builder.delete(TextRange::new(delete_from, delete_to));
31 edit.delete(TextRange::new(delete_from, delete_to));
32 }) 31 })
33} 32}
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs
index 757f6406e..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<()> {
@@ -140,7 +140,7 @@ mod tests {
140 "#, 140 "#,
141 r#" 141 r#"
142 struct Foo {foo: i32, bar: i32}; 142 struct Foo {foo: i32, bar: i32};
143 const test: Foo = <|>Foo {foo: 1, bar: 0} 143 const test: Foo = Foo {foo: 1, bar: 0}
144 "#, 144 "#,
145 ) 145 )
146 } 146 }
@@ -164,7 +164,7 @@ mod tests {
164 164
165 fn f(f: Foo) -> { 165 fn f(f: Foo) -> {
166 match f { 166 match f {
167 <|>Foo { ref mut bar, baz: 0, .. } => (), 167 Foo { ref mut bar, baz: 0, .. } => (),
168 _ => () 168 _ => ()
169 } 169 }
170 } 170 }
@@ -202,7 +202,7 @@ mod tests {
202 impl Foo { 202 impl Foo {
203 fn new() -> Foo { 203 fn new() -> Foo {
204 let foo = String::new(); 204 let foo = String::new();
205 <|>Foo { 205 Foo {
206 foo, 206 foo,
207 bar: foo.clone(), 207 bar: foo.clone(),
208 extra: "Extra field", 208 extra: "Extra field",
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
index 65f5fc6ab..e016f51c3 100644
--- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
@@ -68,7 +68,6 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
68 .indent(IndentLevel::from_node(if_expr.syntax())) 68 .indent(IndentLevel::from_node(if_expr.syntax()))
69 }; 69 };
70 70
71 edit.set_cursor(if_expr.syntax().text_range().start());
72 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); 71 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
73 }) 72 })
74} 73}
@@ -83,7 +82,7 @@ mod tests {
83 fn test_replace_if_let_with_match_unwraps_simple_expressions() { 82 fn test_replace_if_let_with_match_unwraps_simple_expressions() {
84 check_assist( 83 check_assist(
85 replace_if_let_with_match, 84 replace_if_let_with_match,
86 " 85 r#"
87impl VariantData { 86impl VariantData {
88 pub fn is_struct(&self) -> bool { 87 pub fn is_struct(&self) -> bool {
89 if <|>let VariantData::Struct(..) = *self { 88 if <|>let VariantData::Struct(..) = *self {
@@ -92,16 +91,16 @@ impl VariantData {
92 false 91 false
93 } 92 }
94 } 93 }
95} ", 94} "#,
96 " 95 r#"
97impl VariantData { 96impl VariantData {
98 pub fn is_struct(&self) -> bool { 97 pub fn is_struct(&self) -> bool {
99 <|>match *self { 98 match *self {
100 VariantData::Struct(..) => true, 99 VariantData::Struct(..) => true,
101 _ => false, 100 _ => false,
102 } 101 }
103 } 102 }
104} ", 103} "#,
105 ) 104 )
106 } 105 }
107 106
@@ -109,7 +108,7 @@ impl VariantData {
109 fn test_replace_if_let_with_match_doesnt_unwrap_multiline_expressions() { 108 fn test_replace_if_let_with_match_doesnt_unwrap_multiline_expressions() {
110 check_assist( 109 check_assist(
111 replace_if_let_with_match, 110 replace_if_let_with_match,
112 " 111 r#"
113fn foo() { 112fn foo() {
114 if <|>let VariantData::Struct(..) = a { 113 if <|>let VariantData::Struct(..) = a {
115 bar( 114 bar(
@@ -118,10 +117,10 @@ fn foo() {
118 } else { 117 } else {
119 false 118 false
120 } 119 }
121} ", 120} "#,
122 " 121 r#"
123fn foo() { 122fn foo() {
124 <|>match a { 123 match a {
125 VariantData::Struct(..) => { 124 VariantData::Struct(..) => {
126 bar( 125 bar(
127 123 126 123
@@ -129,7 +128,7 @@ fn foo() {
129 } 128 }
130 _ => false, 129 _ => false,
131 } 130 }
132} ", 131} "#,
133 ) 132 )
134 } 133 }
135 134
@@ -137,7 +136,7 @@ fn foo() {
137 fn replace_if_let_with_match_target() { 136 fn replace_if_let_with_match_target() {
138 check_assist_target( 137 check_assist_target(
139 replace_if_let_with_match, 138 replace_if_let_with_match,
140 " 139 r#"
141impl VariantData { 140impl VariantData {
142 pub fn is_struct(&self) -> bool { 141 pub fn is_struct(&self) -> bool {
143 if <|>let VariantData::Struct(..) = *self { 142 if <|>let VariantData::Struct(..) = *self {
@@ -146,7 +145,7 @@ impl VariantData {
146 false 145 false
147 } 146 }
148 } 147 }
149} ", 148} "#,
150 "if let VariantData::Struct(..) = *self { 149 "if let VariantData::Struct(..) = *self {
151 true 150 true
152 } else { 151 } else {
@@ -176,7 +175,7 @@ enum Option<T> { Some(T), None }
176use Option::*; 175use Option::*;
177 176
178fn foo(x: Option<i32>) { 177fn foo(x: Option<i32>) {
179 <|>match x { 178 match x {
180 Some(x) => println!("{}", x), 179 Some(x) => println!("{}", x),
181 None => println!("none"), 180 None => println!("none"),
182 } 181 }
@@ -206,7 +205,7 @@ enum Result<T, E> { Ok(T), Err(E) }
206use Result::*; 205use Result::*;
207 206
208fn foo(x: Result<i32, ()>) { 207fn foo(x: Result<i32, ()>) {
209 <|>match x { 208 match x {
210 Ok(x) => println!("{}", x), 209 Ok(x) => println!("{}", x),
211 Err(_) => println!("none"), 210 Err(_) => println!("none"),
212 } 211 }
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
index 482957dc6..761557ac0 100644
--- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
@@ -58,12 +58,9 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
58 let stmt = make::expr_stmt(if_); 58 let stmt = make::expr_stmt(if_);
59 59
60 let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap(); 60 let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap();
61 let target_offset =
62 let_stmt.syntax().text_range().start() + placeholder.syntax().text_range().start();
63 let stmt = stmt.replace_descendant(placeholder.into(), original_pat); 61 let stmt = stmt.replace_descendant(placeholder.into(), original_pat);
64 62
65 edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); 63 edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
66 edit.set_cursor(target_offset);
67 }) 64 })
68} 65}
69 66
@@ -88,7 +85,7 @@ fn main() {
88enum E<T> { X(T), Y(T) } 85enum E<T> { X(T), Y(T) }
89 86
90fn main() { 87fn main() {
91 if let <|>x = E::X(92) { 88 if let x = E::X(92) {
92 } 89 }
93} 90}
94 ", 91 ",
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
index 1a81d8a0e..0197a8cf0 100644
--- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -39,7 +39,7 @@ pub(crate) fn replace_qualified_name_with_use(
39 target, 39 target,
40 |builder| { 40 |builder| {
41 let path_to_import = hir_path.mod_path().clone(); 41 let path_to_import = hir_path.mod_path().clone();
42 insert_use_statement(path.syntax(), &path_to_import, ctx, builder); 42 insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder());
43 43
44 if let Some(last) = path.segment() { 44 if let Some(last) = path.segment() {
45 // Here we are assuming the assist will provide a correct use statement 45 // Here we are assuming the assist will provide a correct use statement
@@ -89,7 +89,7 @@ std::fmt::Debug<|>
89 " 89 "
90use std::fmt::Debug; 90use std::fmt::Debug;
91 91
92Debug<|> 92Debug
93 ", 93 ",
94 ); 94 );
95 } 95 }
@@ -106,7 +106,7 @@ fn main() {
106 " 106 "
107use std::fmt::Debug; 107use std::fmt::Debug;
108 108
109Debug<|> 109Debug
110 110
111fn main() { 111fn main() {
112} 112}
@@ -130,7 +130,7 @@ use std::fmt::Debug;
130fn main() { 130fn main() {
131} 131}
132 132
133Debug<|> 133Debug
134 ", 134 ",
135 ); 135 );
136 } 136 }
@@ -145,7 +145,7 @@ std::fmt<|>::Debug
145 " 145 "
146use std::fmt; 146use std::fmt;
147 147
148fmt<|>::Debug 148fmt::Debug
149 ", 149 ",
150 ); 150 );
151 } 151 }
@@ -164,7 +164,7 @@ impl std::fmt::Debug<|> for Foo {
164use stdx; 164use stdx;
165use std::fmt::Debug; 165use std::fmt::Debug;
166 166
167impl Debug<|> for Foo { 167impl Debug for Foo {
168} 168}
169 ", 169 ",
170 ); 170 );
@@ -181,7 +181,7 @@ impl std::fmt::Debug<|> for Foo {
181 " 181 "
182use std::fmt::Debug; 182use std::fmt::Debug;
183 183
184impl Debug<|> for Foo { 184impl Debug for Foo {
185} 185}
186 ", 186 ",
187 ); 187 );
@@ -198,7 +198,7 @@ impl Debug<|> for Foo {
198 " 198 "
199 use std::fmt::Debug; 199 use std::fmt::Debug;
200 200
201 impl Debug<|> for Foo { 201 impl Debug for Foo {
202 } 202 }
203 ", 203 ",
204 ); 204 );
@@ -217,7 +217,7 @@ impl std::io<|> for Foo {
217 " 217 "
218use std::{io, fmt}; 218use std::{io, fmt};
219 219
220impl io<|> for Foo { 220impl io for Foo {
221} 221}
222 ", 222 ",
223 ); 223 );
@@ -236,7 +236,7 @@ impl std::fmt::Debug<|> for Foo {
236 " 236 "
237use std::fmt::{self, Debug, }; 237use std::fmt::{self, Debug, };
238 238
239impl Debug<|> for Foo { 239impl Debug for Foo {
240} 240}
241 ", 241 ",
242 ); 242 );
@@ -255,7 +255,7 @@ impl std::fmt<|> for Foo {
255 " 255 "
256use std::fmt::{self, Debug}; 256use std::fmt::{self, Debug};
257 257
258impl fmt<|> for Foo { 258impl fmt for Foo {
259} 259}
260 ", 260 ",
261 ); 261 );
@@ -274,7 +274,7 @@ impl std::fmt::nested<|> for Foo {
274 " 274 "
275use std::fmt::{Debug, nested::{Display, self}}; 275use std::fmt::{Debug, nested::{Display, self}};
276 276
277impl nested<|> for Foo { 277impl nested for Foo {
278} 278}
279", 279",
280 ); 280 );
@@ -293,7 +293,7 @@ impl std::fmt::nested<|> for Foo {
293 " 293 "
294use std::fmt::{Debug, nested::{self, Display}}; 294use std::fmt::{Debug, nested::{self, Display}};
295 295
296impl nested<|> for Foo { 296impl nested for Foo {
297} 297}
298", 298",
299 ); 299 );
@@ -312,7 +312,7 @@ impl std::fmt::nested::Debug<|> for Foo {
312 " 312 "
313use std::fmt::{Debug, nested::{Display, Debug}}; 313use std::fmt::{Debug, nested::{Display, Debug}};
314 314
315impl Debug<|> for Foo { 315impl Debug for Foo {
316} 316}
317", 317",
318 ); 318 );
@@ -331,7 +331,7 @@ impl std::fmt::nested::Display<|> for Foo {
331 " 331 "
332use std::fmt::{nested::Display, Debug}; 332use std::fmt::{nested::Display, Debug};
333 333
334impl Display<|> for Foo { 334impl Display for Foo {
335} 335}
336", 336",
337 ); 337 );
@@ -350,7 +350,7 @@ impl std::fmt::Display<|> for Foo {
350 " 350 "
351use std::fmt::{Display, nested::Debug}; 351use std::fmt::{Display, nested::Debug};
352 352
353impl Display<|> for Foo { 353impl Display for Foo {
354} 354}
355", 355",
356 ); 356 );
@@ -374,7 +374,7 @@ use crate::{
374 AssocItem, 374 AssocItem,
375}; 375};
376 376
377fn foo() { lower<|>::trait_env() } 377fn foo() { lower::trait_env() }
378", 378",
379 ); 379 );
380 } 380 }
@@ -392,7 +392,7 @@ impl foo::Debug<|> for Foo {
392 " 392 "
393use std::fmt as foo; 393use std::fmt as foo;
394 394
395impl Debug<|> for Foo { 395impl Debug for Foo {
396} 396}
397", 397",
398 ); 398 );
@@ -435,7 +435,7 @@ mod foo {
435 mod bar { 435 mod bar {
436 use std::fmt::Debug; 436 use std::fmt::Debug;
437 437
438 Debug<|> 438 Debug
439 } 439 }
440} 440}
441 ", 441 ",
@@ -458,7 +458,7 @@ fn main() {
458use std::fmt::Debug; 458use std::fmt::Debug;
459 459
460fn main() { 460fn main() {
461 Debug<|> 461 Debug
462} 462}
463 ", 463 ",
464 ); 464 );
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
index c4b56f6e9..cff7dfb81 100644
--- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
@@ -9,7 +9,10 @@ use ra_syntax::{
9 AstNode, 9 AstNode,
10}; 10};
11 11
12use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; 12use crate::{
13 utils::{render_snippet, Cursor, TryEnum},
14 AssistContext, AssistId, Assists,
15};
13 16
14// Assist: replace_unwrap_with_match 17// Assist: replace_unwrap_with_match
15// 18//
@@ -29,7 +32,7 @@ use crate::{utils::TryEnum, AssistContext, AssistId, Assists};
29// let x: Result<i32, i32> = Result::Ok(92); 32// let x: Result<i32, i32> = Result::Ok(92);
30// let y = match x { 33// let y = match x {
31// Ok(a) => a, 34// Ok(a) => a,
32// _ => unreachable!(), 35// $0_ => unreachable!(),
33// }; 36// };
34// } 37// }
35// ``` 38// ```
@@ -43,7 +46,7 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext)
43 let ty = ctx.sema.type_of_expr(&caller)?; 46 let ty = ctx.sema.type_of_expr(&caller)?;
44 let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case(); 47 let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case();
45 let target = method_call.syntax().text_range(); 48 let target = method_call.syntax().text_range();
46 acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |edit| { 49 acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |builder| {
47 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); 50 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
48 let it = make::bind_pat(make::name("a")).into(); 51 let it = make::bind_pat(make::name("a")).into();
49 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); 52 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
@@ -51,23 +54,37 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext)
51 let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); 54 let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
52 let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); 55 let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
53 56
54 let unreachable_call = make::unreachable_macro_call().into(); 57 let unreachable_call = make::expr_unreachable();
55 let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); 58 let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call);
56 59
57 let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); 60 let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
58 let match_expr = make::expr_match(caller.clone(), match_arm_list) 61 let match_expr = make::expr_match(caller.clone(), match_arm_list)
59 .indent(IndentLevel::from_node(method_call.syntax())); 62 .indent(IndentLevel::from_node(method_call.syntax()));
60 63
61 edit.set_cursor(caller.syntax().text_range().start()); 64 let range = method_call.syntax().text_range();
62 edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); 65 match ctx.config.snippet_cap {
66 Some(cap) => {
67 let err_arm = match_expr
68 .syntax()
69 .descendants()
70 .filter_map(ast::MatchArm::cast)
71 .last()
72 .unwrap();
73 let snippet =
74 render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax()));
75 builder.replace_snippet(cap, range, snippet)
76 }
77 None => builder.replace(range, match_expr.to_string()),
78 }
63 }) 79 })
64} 80}
65 81
66#[cfg(test)] 82#[cfg(test)]
67mod tests { 83mod tests {
68 use super::*;
69 use crate::tests::{check_assist, check_assist_target}; 84 use crate::tests::{check_assist, check_assist_target};
70 85
86 use super::*;
87
71 #[test] 88 #[test]
72 fn test_replace_result_unwrap_with_match() { 89 fn test_replace_result_unwrap_with_match() {
73 check_assist( 90 check_assist(
@@ -85,9 +102,9 @@ enum Result<T, E> { Ok(T), Err(E) }
85fn i<T>(a: T) -> T { a } 102fn i<T>(a: T) -> T { a }
86fn main() { 103fn main() {
87 let x: Result<i32, i32> = Result::Ok(92); 104 let x: Result<i32, i32> = Result::Ok(92);
88 let y = <|>match i(x) { 105 let y = match i(x) {
89 Ok(a) => a, 106 Ok(a) => a,
90 _ => unreachable!(), 107 $0_ => unreachable!(),
91 }; 108 };
92} 109}
93 ", 110 ",
@@ -111,9 +128,9 @@ enum Option<T> { Some(T), None }
111fn i<T>(a: T) -> T { a } 128fn i<T>(a: T) -> T { a }
112fn main() { 129fn main() {
113 let x = Option::Some(92); 130 let x = Option::Some(92);
114 let y = <|>match i(x) { 131 let y = match i(x) {
115 Some(a) => a, 132 Some(a) => a,
116 _ => unreachable!(), 133 $0_ => unreachable!(),
117 }; 134 };
118} 135}
119 ", 136 ",
@@ -137,9 +154,9 @@ enum Result<T, E> { Ok(T), Err(E) }
137fn i<T>(a: T) -> T { a } 154fn i<T>(a: T) -> T { a }
138fn main() { 155fn main() {
139 let x: Result<i32, i32> = Result::Ok(92); 156 let x: Result<i32, i32> = Result::Ok(92);
140 let y = <|>match i(x) { 157 let y = match i(x) {
141 Ok(a) => a, 158 Ok(a) => a,
142 _ => unreachable!(), 159 $0_ => unreachable!(),
143 }.count_zeroes(); 160 }.count_zeroes();
144} 161}
145 ", 162 ",
diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs
index b2757e50c..c7a874480 100644
--- a/crates/ra_assists/src/handlers/split_import.rs
+++ b/crates/ra_assists/src/handlers/split_import.rs
@@ -26,12 +26,10 @@ pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
26 if new_tree == use_tree { 26 if new_tree == use_tree {
27 return None; 27 return None;
28 } 28 }
29 let cursor = ctx.offset();
30 29
31 let target = colon_colon.text_range(); 30 let target = colon_colon.text_range();
32 acc.add(AssistId("split_import"), "Split import", target, |edit| { 31 acc.add(AssistId("split_import"), "Split import", target, |edit| {
33 edit.replace_ast(use_tree, new_tree); 32 edit.replace_ast(use_tree, new_tree);
34 edit.set_cursor(cursor);
35 }) 33 })
36} 34}
37 35
@@ -46,7 +44,7 @@ mod tests {
46 check_assist( 44 check_assist(
47 split_import, 45 split_import,
48 "use crate::<|>db::RootDatabase;", 46 "use crate::<|>db::RootDatabase;",
49 "use crate::<|>{db::RootDatabase};", 47 "use crate::{db::RootDatabase};",
50 ) 48 )
51 } 49 }
52 50
@@ -55,7 +53,7 @@ mod tests {
55 check_assist( 53 check_assist(
56 split_import, 54 split_import,
57 "use crate:<|>:db::{RootDatabase, FileSymbol}", 55 "use crate:<|>:db::{RootDatabase, FileSymbol}",
58 "use crate:<|>:{db::{RootDatabase, FileSymbol}}", 56 "use crate::{db::{RootDatabase, FileSymbol}}",
59 ) 57 )
60 } 58 }
61 59
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs
index e52ec557e..8440c7d0f 100644
--- a/crates/ra_assists/src/handlers/unwrap_block.rs
+++ b/crates/ra_assists/src/handlers/unwrap_block.rs
@@ -1,8 +1,10 @@
1use crate::{AssistContext, AssistId, Assists};
2
3use ast::{ElseBranch, Expr, LoopBodyOwner};
4use ra_fmt::unwrap_trivial_block; 1use ra_fmt::unwrap_trivial_block;
5use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; 2use ra_syntax::{
3 ast::{self, ElseBranch, Expr, LoopBodyOwner},
4 match_ast, AstNode, TextRange, T,
5};
6
7use crate::{AssistContext, AssistId, Assists};
6 8
7// Assist: unwrap_block 9// Assist: unwrap_block
8// 10//
@@ -60,7 +62,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
60 let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); 62 let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
61 let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); 63 let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end());
62 64
63 edit.set_cursor(ancestor_then_branch.syntax().text_range().end());
64 edit.delete(range_to_del_rest); 65 edit.delete(range_to_del_rest);
65 edit.delete(range_to_del_else_if); 66 edit.delete(range_to_del_else_if);
66 edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); 67 edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{']));
@@ -77,7 +78,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
77 return acc.add(assist_id, assist_label, target, |edit| { 78 return acc.add(assist_id, assist_label, target, |edit| {
78 let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); 79 let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
79 80
80 edit.set_cursor(then_branch.syntax().text_range().end());
81 edit.delete(range_to_del); 81 edit.delete(range_to_del);
82 edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{'])); 82 edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{']));
83 }); 83 });
@@ -95,8 +95,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
95 95
96 let target = expr_to_unwrap.syntax().text_range(); 96 let target = expr_to_unwrap.syntax().text_range();
97 acc.add(assist_id, assist_label, target, |edit| { 97 acc.add(assist_id, assist_label, target, |edit| {
98 edit.set_cursor(expr.syntax().text_range().start());
99
100 edit.replace( 98 edit.replace(
101 expr.syntax().text_range(), 99 expr.syntax().text_range(),
102 update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), 100 update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']),
@@ -152,7 +150,7 @@ mod tests {
152 r#" 150 r#"
153 fn main() { 151 fn main() {
154 bar(); 152 bar();
155 <|>foo(); 153 foo();
156 154
157 //comment 155 //comment
158 bar(); 156 bar();
@@ -186,7 +184,7 @@ mod tests {
186 184
187 //comment 185 //comment
188 bar(); 186 bar();
189 }<|> 187 }
190 println!("bar"); 188 println!("bar");
191 } 189 }
192 "#, 190 "#,
@@ -220,7 +218,7 @@ mod tests {
220 218
221 //comment 219 //comment
222 //bar(); 220 //bar();
223 }<|> 221 }
224 println!("bar"); 222 println!("bar");
225 } 223 }
226 "#, 224 "#,
@@ -256,7 +254,7 @@ mod tests {
256 //bar(); 254 //bar();
257 } else if false { 255 } else if false {
258 println!("bar"); 256 println!("bar");
259 }<|> 257 }
260 println!("foo"); 258 println!("foo");
261 } 259 }
262 "#, 260 "#,
@@ -296,7 +294,7 @@ mod tests {
296 println!("bar"); 294 println!("bar");
297 } else if true { 295 } else if true {
298 println!("foo"); 296 println!("foo");
299 }<|> 297 }
300 println!("else"); 298 println!("else");
301 } 299 }
302 "#, 300 "#,
@@ -334,7 +332,7 @@ mod tests {
334 //bar(); 332 //bar();
335 } else if false { 333 } else if false {
336 println!("bar"); 334 println!("bar");
337 }<|> 335 }
338 println!("foo"); 336 println!("foo");
339 } 337 }
340 "#, 338 "#,
@@ -381,7 +379,7 @@ mod tests {
381 "#, 379 "#,
382 r#" 380 r#"
383 fn main() { 381 fn main() {
384 <|>if true { 382 if true {
385 foo(); 383 foo();
386 384
387 //comment 385 //comment
@@ -415,7 +413,7 @@ mod tests {
415 r#" 413 r#"
416 fn main() { 414 fn main() {
417 for i in 0..5 { 415 for i in 0..5 {
418 <|>foo(); 416 foo();
419 417
420 //comment 418 //comment
421 bar(); 419 bar();
@@ -445,7 +443,7 @@ mod tests {
445 "#, 443 "#,
446 r#" 444 r#"
447 fn main() { 445 fn main() {
448 <|>if true { 446 if true {
449 foo(); 447 foo();
450 448
451 //comment 449 //comment
@@ -478,7 +476,7 @@ mod tests {
478 "#, 476 "#,
479 r#" 477 r#"
480 fn main() { 478 fn main() {
481 <|>if true { 479 if true {
482 foo(); 480 foo();
483 481
484 //comment 482 //comment
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index b6dc7cb1b..464bc03dd 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -10,8 +10,8 @@ macro_rules! eprintln {
10 ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; 10 ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
11} 11}
12 12
13mod assist_config;
13mod assist_context; 14mod assist_context;
14mod marks;
15#[cfg(test)] 15#[cfg(test)]
16mod tests; 16mod tests;
17pub mod utils; 17pub mod utils;
@@ -24,6 +24,8 @@ use ra_syntax::TextRange;
24 24
25pub(crate) use crate::assist_context::{AssistContext, Assists}; 25pub(crate) use crate::assist_context::{AssistContext, Assists};
26 26
27pub use assist_config::AssistConfig;
28
27/// Unique identifier of the assist, should not be shown to the user 29/// Unique identifier of the assist, should not be shown to the user
28/// directly. 30/// directly.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)] 31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -54,9 +56,9 @@ impl Assist {
54 /// 56 ///
55 /// Assists are returned in the "unresolved" state, that is only labels are 57 /// Assists are returned in the "unresolved" state, that is only labels are
56 /// returned, without actual edits. 58 /// returned, without actual edits.
57 pub fn unresolved(db: &RootDatabase, range: FileRange) -> Vec<Assist> { 59 pub fn unresolved(db: &RootDatabase, config: &AssistConfig, range: FileRange) -> Vec<Assist> {
58 let sema = Semantics::new(db); 60 let sema = Semantics::new(db);
59 let ctx = AssistContext::new(sema, range); 61 let ctx = AssistContext::new(sema, config, range);
60 let mut acc = Assists::new_unresolved(&ctx); 62 let mut acc = Assists::new_unresolved(&ctx);
61 handlers::all().iter().for_each(|handler| { 63 handlers::all().iter().for_each(|handler| {
62 handler(&mut acc, &ctx); 64 handler(&mut acc, &ctx);
@@ -68,9 +70,13 @@ impl Assist {
68 /// 70 ///
69 /// Assists are returned in the "resolved" state, that is with edit fully 71 /// Assists are returned in the "resolved" state, that is with edit fully
70 /// computed. 72 /// computed.
71 pub fn resolved(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { 73 pub fn resolved(
74 db: &RootDatabase,
75 config: &AssistConfig,
76 range: FileRange,
77 ) -> Vec<ResolvedAssist> {
72 let sema = Semantics::new(db); 78 let sema = Semantics::new(db);
73 let ctx = AssistContext::new(sema, range); 79 let ctx = AssistContext::new(sema, config, range);
74 let mut acc = Assists::new_resolved(&ctx); 80 let mut acc = Assists::new_resolved(&ctx);
75 handlers::all().iter().for_each(|handler| { 81 handlers::all().iter().for_each(|handler| {
76 handler(&mut acc, &ctx); 82 handler(&mut acc, &ctx);
@@ -103,12 +109,14 @@ mod handlers {
103 mod add_impl; 109 mod add_impl;
104 mod add_missing_impl_members; 110 mod add_missing_impl_members;
105 mod add_new; 111 mod add_new;
112 mod add_turbo_fish;
106 mod apply_demorgan; 113 mod apply_demorgan;
107 mod auto_import; 114 mod auto_import;
108 mod change_return_type_to_result; 115 mod change_return_type_to_result;
109 mod change_visibility; 116 mod change_visibility;
110 mod early_return; 117 mod early_return;
111 mod fill_match_arms; 118 mod fill_match_arms;
119 mod fix_visibility;
112 mod flip_binexpr; 120 mod flip_binexpr;
113 mod flip_comma; 121 mod flip_comma;
114 mod flip_trait_bound; 122 mod flip_trait_bound;
@@ -140,12 +148,14 @@ mod handlers {
140 add_function::add_function, 148 add_function::add_function,
141 add_impl::add_impl, 149 add_impl::add_impl,
142 add_new::add_new, 150 add_new::add_new,
151 add_turbo_fish::add_turbo_fish,
143 apply_demorgan::apply_demorgan, 152 apply_demorgan::apply_demorgan,
144 auto_import::auto_import, 153 auto_import::auto_import,
145 change_return_type_to_result::change_return_type_to_result, 154 change_return_type_to_result::change_return_type_to_result,
146 change_visibility::change_visibility, 155 change_visibility::change_visibility,
147 early_return::convert_to_guarded_return, 156 early_return::convert_to_guarded_return,
148 fill_match_arms::fill_match_arms, 157 fill_match_arms::fill_match_arms,
158 fix_visibility::fix_visibility,
149 flip_binexpr::flip_binexpr, 159 flip_binexpr::flip_binexpr,
150 flip_comma::flip_comma, 160 flip_comma::flip_comma,
151 flip_trait_bound::flip_trait_bound, 161 flip_trait_bound::flip_trait_bound,
diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs
deleted file mode 100644
index 8d910205f..000000000
--- a/crates/ra_assists/src/marks.rs
+++ /dev/null
@@ -1,12 +0,0 @@
1//! See test_utils/src/marks.rs
2
3test_utils::marks![
4 introduce_var_in_comment_is_not_applicable
5 test_introduce_var_expr_stmt
6 test_introduce_var_last_expr
7 not_applicable_outside_of_bind_pat
8 test_not_inline_mut_variable
9 test_not_applicable_if_variable_unused
10 change_visibility_field_false_positive
11 test_add_from_impl_already_exists
12];
diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs
index a3eacb8f1..62dd3547f 100644
--- a/crates/ra_assists/src/tests.rs
+++ b/crates/ra_assists/src/tests.rs
@@ -7,11 +7,10 @@ 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, AssistContext, Assists}; 13use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists};
15 14
16pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { 15pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
17 let (mut db, file_id) = RootDatabase::with_single_file(text); 16 let (mut db, file_id) = RootDatabase::with_single_file(text);
@@ -41,14 +40,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
41 let (db, file_id) = crate::tests::with_single_file(&before); 40 let (db, file_id) = crate::tests::with_single_file(&before);
42 let frange = FileRange { file_id, range: selection.into() }; 41 let frange = FileRange { file_id, range: selection.into() };
43 42
44 let mut assist = Assist::resolved(&db, frange) 43 let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange)
45 .into_iter() 44 .into_iter()
46 .find(|assist| assist.assist.id.0 == assist_id) 45 .find(|assist| assist.assist.id.0 == assist_id)
47 .unwrap_or_else(|| { 46 .unwrap_or_else(|| {
48 panic!( 47 panic!(
49 "\n\nAssist is not applicable: {}\nAvailable assists: {}", 48 "\n\nAssist is not applicable: {}\nAvailable assists: {}",
50 assist_id, 49 assist_id,
51 Assist::resolved(&db, frange) 50 Assist::resolved(&db, &AssistConfig::default(), frange)
52 .into_iter() 51 .into_iter()
53 .map(|assist| assist.assist.id.0) 52 .map(|assist| assist.assist.id.0)
54 .collect::<Vec<_>>() 53 .collect::<Vec<_>>()
@@ -90,7 +89,8 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) {
90 let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() }; 89 let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
91 90
92 let sema = Semantics::new(&db); 91 let sema = Semantics::new(&db);
93 let ctx = AssistContext::new(sema, frange); 92 let config = AssistConfig::default();
93 let ctx = AssistContext::new(sema, &config, frange);
94 let mut acc = Assists::new_resolved(&ctx); 94 let mut acc = Assists::new_resolved(&ctx);
95 handler(&mut acc, &ctx); 95 handler(&mut acc, &ctx);
96 let mut res = acc.finish_resolved(); 96 let mut res = acc.finish_resolved();
@@ -102,20 +102,6 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) {
102 102
103 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();
104 change.edit.apply(&mut actual); 104 change.edit.apply(&mut actual);
105
106 match source_change.cursor_position {
107 None => {
108 if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
109 let off = change
110 .edit
111 .apply_to_offset(before_cursor_pos)
112 .expect("cursor position is affected by the edit");
113 actual = add_cursor(&actual, off)
114 }
115 }
116 Some(off) => actual = add_cursor(&actual, off.offset),
117 };
118
119 assert_eq_text!(after, &actual); 105 assert_eq_text!(after, &actual);
120 } 106 }
121 (Some(assist), ExpectedResult::Target(target)) => { 107 (Some(assist), ExpectedResult::Target(target)) => {
@@ -136,7 +122,7 @@ fn assist_order_field_struct() {
136 let (before_cursor_pos, before) = extract_offset(before); 122 let (before_cursor_pos, before) = extract_offset(before);
137 let (db, file_id) = with_single_file(&before); 123 let (db, file_id) = with_single_file(&before);
138 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; 124 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) };
139 let assists = Assist::resolved(&db, frange); 125 let assists = Assist::resolved(&db, &AssistConfig::default(), frange);
140 let mut assists = assists.iter(); 126 let mut assists = assists.iter();
141 127
142 assert_eq!( 128 assert_eq!(
@@ -159,7 +145,7 @@ fn assist_order_if_expr() {
159 let (range, before) = extract_range(before); 145 let (range, before) = extract_range(before);
160 let (db, file_id) = with_single_file(&before); 146 let (db, file_id) = with_single_file(&before);
161 let frange = FileRange { file_id, range }; 147 let frange = FileRange { file_id, range };
162 let assists = Assist::resolved(&db, frange); 148 let assists = Assist::resolved(&db, &AssistConfig::default(), frange);
163 let mut assists = assists.iter(); 149 let mut assists = assists.iter();
164 150
165 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); 151 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs
index 972dbd251..250e56a69 100644
--- a/crates/ra_assists/src/tests/generated.rs
+++ b/crates/ra_assists/src/tests/generated.rs
@@ -15,7 +15,7 @@ struct S;
15struct S; 15struct S;
16 16
17impl Debug for S { 17impl Debug for S {
18 18 $0
19} 19}
20"#####, 20"#####,
21 ) 21 )
@@ -32,7 +32,7 @@ struct Point {
32} 32}
33"#####, 33"#####,
34 r#####" 34 r#####"
35#[derive()] 35#[derive($0)]
36struct Point { 36struct Point {
37 x: u32, 37 x: u32,
38 y: u32, 38 y: u32,
@@ -78,7 +78,7 @@ fn foo() {
78} 78}
79 79
80fn bar(arg: &str, baz: Baz) { 80fn bar(arg: &str, baz: Baz) {
81 todo!() 81 ${0:todo!()}
82} 82}
83 83
84"#####, 84"#####,
@@ -108,16 +108,16 @@ fn doctest_add_impl() {
108 "add_impl", 108 "add_impl",
109 r#####" 109 r#####"
110struct Ctx<T: Clone> { 110struct Ctx<T: Clone> {
111 data: T,<|> 111 data: T,<|>
112} 112}
113"#####, 113"#####,
114 r#####" 114 r#####"
115struct Ctx<T: Clone> { 115struct Ctx<T: Clone> {
116 data: T, 116 data: T,
117} 117}
118 118
119impl<T: Clone> Ctx<T> { 119impl<T: Clone> Ctx<T> {
120 120 $0
121} 121}
122"#####, 122"#####,
123 ) 123 )
@@ -150,7 +150,7 @@ trait Trait {
150impl Trait for () { 150impl Trait for () {
151 Type X = (); 151 Type X = ();
152 fn foo(&self) {} 152 fn foo(&self) {}
153 fn bar(&self) {} 153 $0fn bar(&self) {}
154 154
155} 155}
156"#####, 156"#####,
@@ -181,7 +181,7 @@ trait Trait<T> {
181 181
182impl Trait<u32> for () { 182impl Trait<u32> for () {
183 fn foo(&self) -> u32 { 183 fn foo(&self) -> u32 {
184 todo!() 184 ${0:todo!()}
185 } 185 }
186 186
187} 187}
@@ -204,7 +204,7 @@ struct Ctx<T: Clone> {
204} 204}
205 205
206impl<T: Clone> Ctx<T> { 206impl<T: Clone> Ctx<T> {
207 fn new(data: T) -> Self { Self { data } } 207 fn $0new(data: T) -> Self { Self { data } }
208} 208}
209 209
210"#####, 210"#####,
@@ -212,6 +212,25 @@ impl<T: Clone> Ctx<T> {
212} 212}
213 213
214#[test] 214#[test]
215fn doctest_add_turbo_fish() {
216 check_doc_test(
217 "add_turbo_fish",
218 r#####"
219fn make<T>() -> T { todo!() }
220fn main() {
221 let x = make<|>();
222}
223"#####,
224 r#####"
225fn make<T>() -> T { todo!() }
226fn main() {
227 let x = make::<${0:_}>();
228}
229"#####,
230 )
231}
232
233#[test]
215fn doctest_apply_demorgan() { 234fn doctest_apply_demorgan() {
216 check_doc_test( 235 check_doc_test(
217 "apply_demorgan", 236 "apply_demorgan",
@@ -257,7 +276,7 @@ fn doctest_change_return_type_to_result() {
257fn foo() -> i32<|> { 42i32 } 276fn foo() -> i32<|> { 42i32 }
258"#####, 277"#####,
259 r#####" 278 r#####"
260fn foo() -> Result<i32, > { Ok(42i32) } 279fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
261"#####, 280"#####,
262 ) 281 )
263} 282}
@@ -317,7 +336,7 @@ enum Action { Move { distance: u32 }, Stop }
317 336
318fn handle(action: Action) { 337fn handle(action: Action) {
319 match action { 338 match action {
320 Action::Move { distance } => {} 339 $0Action::Move { distance } => {}
321 Action::Stop => {} 340 Action::Stop => {}
322 } 341 }
323} 342}
@@ -326,6 +345,29 @@ fn handle(action: Action) {
326} 345}
327 346
328#[test] 347#[test]
348fn doctest_fix_visibility() {
349 check_doc_test(
350 "fix_visibility",
351 r#####"
352mod m {
353 fn frobnicate() {}
354}
355fn main() {
356 m::frobnicate<|>() {}
357}
358"#####,
359 r#####"
360mod m {
361 $0pub(crate) fn frobnicate() {}
362}
363fn main() {
364 m::frobnicate() {}
365}
366"#####,
367 )
368}
369
370#[test]
329fn doctest_flip_binexpr() { 371fn doctest_flip_binexpr() {
330 check_doc_test( 372 check_doc_test(
331 "flip_binexpr", 373 "flip_binexpr",
@@ -401,7 +443,7 @@ fn main() {
401"#####, 443"#####,
402 r#####" 444 r#####"
403fn main() { 445fn main() {
404 let var_name = (1 + 2); 446 let $0var_name = (1 + 2);
405 var_name * 4; 447 var_name * 4;
406} 448}
407"#####, 449"#####,
@@ -722,7 +764,7 @@ fn main() {
722 let x: Result<i32, i32> = Result::Ok(92); 764 let x: Result<i32, i32> = Result::Ok(92);
723 let y = match x { 765 let y = match x {
724 Ok(a) => a, 766 Ok(a) => a,
725 _ => unreachable!(), 767 $0_ => unreachable!(),
726 }; 768 };
727} 769}
728"#####, 770"#####,
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs
index f3fc92ebf..0038a9764 100644
--- a/crates/ra_assists/src/utils.rs
+++ b/crates/ra_assists/src/utils.rs
@@ -1,18 +1,57 @@
1//! Assorted functions shared by several assists. 1//! Assorted functions shared by several assists.
2pub(crate) mod insert_use; 2pub(crate) mod insert_use;
3 3
4use std::iter; 4use std::{iter, ops};
5 5
6use hir::{Adt, Crate, Semantics, Trait, Type}; 6use hir::{Adt, Crate, Enum, ScopeDef, Semantics, Trait, Type};
7use ra_ide_db::RootDatabase; 7use ra_ide_db::RootDatabase;
8use ra_syntax::{ 8use ra_syntax::{
9 ast::{self, make, NameOwner}, 9 ast::{self, make, NameOwner},
10 AstNode, T, 10 AstNode, SyntaxNode, T,
11}; 11};
12use rustc_hash::FxHashSet; 12use rustc_hash::FxHashSet;
13 13
14use crate::assist_config::SnippetCap;
15
14pub(crate) use insert_use::insert_use_statement; 16pub(crate) use insert_use::insert_use_statement;
15 17
18#[derive(Clone, Copy, Debug)]
19pub(crate) enum Cursor<'a> {
20 Replace(&'a SyntaxNode),
21 Before(&'a SyntaxNode),
22}
23
24impl<'a> Cursor<'a> {
25 fn node(self) -> &'a SyntaxNode {
26 match self {
27 Cursor::Replace(node) | Cursor::Before(node) => node,
28 }
29 }
30}
31
32pub(crate) fn render_snippet(_cap: SnippetCap, node: &SyntaxNode, cursor: Cursor) -> String {
33 assert!(cursor.node().ancestors().any(|it| it == *node));
34 let range = cursor.node().text_range() - node.text_range().start();
35 let range: ops::Range<usize> = range.into();
36
37 let mut placeholder = cursor.node().to_string();
38 escape(&mut placeholder);
39 let tab_stop = match cursor {
40 Cursor::Replace(placeholder) => format!("${{0:{}}}", placeholder),
41 Cursor::Before(placeholder) => format!("$0{}", placeholder),
42 };
43
44 let mut buf = node.to_string();
45 buf.replace_range(range, &tab_stop);
46 return buf;
47
48 fn escape(buf: &mut String) {
49 stdx::replace(buf, '{', r"\{");
50 stdx::replace(buf, '}', r"\}");
51 stdx::replace(buf, '$', r"\$");
52 }
53}
54
16pub fn get_missing_assoc_items( 55pub fn get_missing_assoc_items(
17 sema: &Semantics<RootDatabase>, 56 sema: &Semantics<RootDatabase>,
18 impl_def: &ast::ImplDef, 57 impl_def: &ast::ImplDef,
@@ -161,13 +200,19 @@ impl FamousDefs<'_, '_> {
161 #[cfg(test)] 200 #[cfg(test)]
162 pub(crate) const FIXTURE: &'static str = r#" 201 pub(crate) const FIXTURE: &'static str = r#"
163//- /libcore.rs crate:core 202//- /libcore.rs crate:core
164pub mod convert{ 203pub mod convert {
165 pub trait From<T> { 204 pub trait From<T> {
166 fn from(T) -> Self; 205 fn from(T) -> Self;
167 } 206 }
168} 207}
169 208
170pub mod prelude { pub use crate::convert::From } 209pub mod option {
210 pub enum Option<T> { None, Some(T)}
211}
212
213pub mod prelude {
214 pub use crate::{convert::From, option::Option::{self, *}};
215}
171#[prelude_import] 216#[prelude_import]
172pub use prelude::*; 217pub use prelude::*;
173"#; 218"#;
@@ -176,7 +221,25 @@ pub use prelude::*;
176 self.find_trait("core:convert:From") 221 self.find_trait("core:convert:From")
177 } 222 }
178 223
224 pub(crate) fn core_option_Option(&self) -> Option<Enum> {
225 self.find_enum("core:option:Option")
226 }
227
179 fn find_trait(&self, path: &str) -> Option<Trait> { 228 fn find_trait(&self, path: &str) -> Option<Trait> {
229 match self.find_def(path)? {
230 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
231 _ => None,
232 }
233 }
234
235 fn find_enum(&self, path: &str) -> Option<Enum> {
236 match self.find_def(path)? {
237 hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it),
238 _ => None,
239 }
240 }
241
242 fn find_def(&self, path: &str) -> Option<ScopeDef> {
180 let db = self.0.db; 243 let db = self.0.db;
181 let mut path = path.split(':'); 244 let mut path = path.split(':');
182 let trait_ = path.next_back()?; 245 let trait_ = path.next_back()?;
@@ -201,9 +264,6 @@ pub use prelude::*;
201 } 264 }
202 let def = 265 let def =
203 module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1; 266 module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1;
204 match def { 267 Some(def)
205 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
206 _ => None,
207 }
208 } 268 }
209} 269}
diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs
index 1214e3cd4..0ee43482f 100644
--- a/crates/ra_assists/src/utils/insert_use.rs
+++ b/crates/ra_assists/src/utils/insert_use.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11}; 11};
12use ra_text_edit::TextEditBuilder; 12use ra_text_edit::TextEditBuilder;
13 13
14use crate::assist_context::{AssistBuilder, AssistContext}; 14use crate::assist_context::AssistContext;
15 15
16/// Creates and inserts a use statement for the given path to import. 16/// Creates and inserts a use statement for the given path to import.
17/// The use statement is inserted in the scope most appropriate to the 17/// The use statement is inserted in the scope most appropriate to the
@@ -21,7 +21,7 @@ pub(crate) fn insert_use_statement(
21 position: &SyntaxNode, 21 position: &SyntaxNode,
22 path_to_import: &ModPath, 22 path_to_import: &ModPath,
23 ctx: &AssistContext, 23 ctx: &AssistContext,
24 builder: &mut AssistBuilder, 24 builder: &mut TextEditBuilder,
25) { 25) {
26 let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>(); 26 let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>();
27 let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| { 27 let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| {
@@ -33,7 +33,7 @@ pub(crate) fn insert_use_statement(
33 33
34 if let Some(container) = container { 34 if let Some(container) = container {
35 let action = best_action_for_target(container, position.clone(), &target); 35 let action = best_action_for_target(container, position.clone(), &target);
36 make_assist(&action, &target, builder.text_edit_builder()); 36 make_assist(&action, &target, builder);
37 } 37 }
38} 38}
39 39
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index c69e0efea..e08d62dd6 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -15,7 +15,7 @@ use ra_syntax::{
15 }, 15 },
16 AstNode, AstPtr, 16 AstNode, AstPtr,
17}; 17};
18use test_utils::tested_by; 18use test_utils::mark;
19 19
20use crate::{ 20use crate::{
21 adt::StructKind, 21 adt::StructKind,
@@ -226,7 +226,7 @@ impl ExprCollector<'_> {
226 None => self.collect_expr_opt(condition.expr()), 226 None => self.collect_expr_opt(condition.expr()),
227 // if let -- desugar to match 227 // if let -- desugar to match
228 Some(pat) => { 228 Some(pat) => {
229 tested_by!(infer_resolve_while_let); 229 mark::hit!(infer_resolve_while_let);
230 let pat = self.collect_pat(pat); 230 let pat = self.collect_pat(pat);
231 let match_expr = self.collect_expr_opt(condition.expr()); 231 let match_expr = self.collect_expr_opt(condition.expr());
232 let placeholder_pat = self.missing_pat(); 232 let placeholder_pat = self.missing_pat();
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs
index 86f953c80..09e92b74e 100644
--- a/crates/ra_hir_def/src/body/scope.rs
+++ b/crates/ra_hir_def/src/body/scope.rs
@@ -174,7 +174,7 @@ mod tests {
174 use hir_expand::{name::AsName, InFile}; 174 use hir_expand::{name::AsName, InFile};
175 use ra_db::{fixture::WithFixture, FileId, SourceDatabase}; 175 use ra_db::{fixture::WithFixture, FileId, SourceDatabase};
176 use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; 176 use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
177 use test_utils::{assert_eq_text, covers, extract_offset}; 177 use test_utils::{assert_eq_text, extract_offset, mark};
178 178
179 use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId}; 179 use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId};
180 180
@@ -388,7 +388,7 @@ mod tests {
388 388
389 #[test] 389 #[test]
390 fn while_let_desugaring() { 390 fn while_let_desugaring() {
391 covers!(infer_resolve_while_let); 391 mark::check!(infer_resolve_while_let);
392 do_check_local_name( 392 do_check_local_name(
393 r#" 393 r#"
394fn test() { 394fn test() {
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
index 498a4c917..945a0025e 100644
--- a/crates/ra_hir_def/src/db.rs
+++ b/crates/ra_hir_def/src/db.rs
@@ -17,6 +17,7 @@ use crate::{
17 item_scope::ItemInNs, 17 item_scope::ItemInNs,
18 lang_item::{LangItemTarget, LangItems}, 18 lang_item::{LangItemTarget, LangItems},
19 nameres::{raw::RawItems, CrateDefMap}, 19 nameres::{raw::RawItems, CrateDefMap},
20 path::ModPath,
20 visibility::Visibility, 21 visibility::Visibility,
21 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, 22 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
22 GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, 23 GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
@@ -112,12 +113,15 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
112 #[salsa::invoke(Documentation::documentation_query)] 113 #[salsa::invoke(Documentation::documentation_query)]
113 fn documentation(&self, def: AttrDefId) -> Option<Documentation>; 114 fn documentation(&self, def: AttrDefId) -> Option<Documentation>;
114 115
115 #[salsa::invoke(find_path::importable_locations_in_crate)] 116 #[salsa::invoke(find_path::importable_locations_of_query)]
116 fn importable_locations_of( 117 fn importable_locations_of(
117 &self, 118 &self,
118 item: ItemInNs, 119 item: ItemInNs,
119 krate: CrateId, 120 krate: CrateId,
120 ) -> Arc<[(ModuleId, Name, Visibility)]>; 121 ) -> Arc<[(ModuleId, Name, Visibility)]>;
122
123 #[salsa::invoke(find_path::find_path_inner_query)]
124 fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath>;
121} 125}
122 126
123fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 127fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index 1ca20fabd..4db798473 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -1,5 +1,11 @@
1//! An algorithm to find a path to refer to a certain item. 1//! An algorithm to find a path to refer to a certain item.
2 2
3use std::sync::Arc;
4
5use hir_expand::name::{known, AsName, Name};
6use ra_prof::profile;
7use test_utils::mark;
8
3use crate::{ 9use crate::{
4 db::DefDatabase, 10 db::DefDatabase,
5 item_scope::ItemInNs, 11 item_scope::ItemInNs,
@@ -7,26 +13,28 @@ use crate::{
7 visibility::Visibility, 13 visibility::Visibility,
8 CrateId, ModuleDefId, ModuleId, 14 CrateId, ModuleDefId, ModuleId,
9}; 15};
10use hir_expand::name::{known, AsName, Name}; 16
11use std::sync::Arc; 17// FIXME: handle local items
12use test_utils::tested_by; 18
19/// Find a path that can be used to refer to a certain item. This can depend on
20/// *from where* you're referring to the item, hence the `from` parameter.
21pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
22 let _p = profile("find_path");
23 db.find_path_inner(item, from, MAX_PATH_LEN)
24}
13 25
14const MAX_PATH_LEN: usize = 15; 26const MAX_PATH_LEN: usize = 15;
15 27
16impl ModPath { 28impl ModPath {
17 fn starts_with_std(&self) -> bool { 29 fn starts_with_std(&self) -> bool {
18 self.segments.first().filter(|&first_segment| first_segment == &known::std).is_some() 30 self.segments.first() == Some(&known::std)
19 } 31 }
20 32
21 // When std library is present, paths starting with `std::` 33 // When std library is present, paths starting with `std::`
22 // should be preferred over paths starting with `core::` and `alloc::` 34 // should be preferred over paths starting with `core::` and `alloc::`
23 fn can_start_with_std(&self) -> bool { 35 fn can_start_with_std(&self) -> bool {
24 self.segments 36 let first_segment = self.segments.first();
25 .first() 37 first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
26 .filter(|&first_segment| {
27 first_segment == &known::alloc || first_segment == &known::core
28 })
29 .is_some()
30 } 38 }
31 39
32 fn len(&self) -> usize { 40 fn len(&self) -> usize {
@@ -41,16 +49,7 @@ impl ModPath {
41 } 49 }
42} 50}
43 51
44// FIXME: handle local items 52pub(crate) fn find_path_inner_query(
45
46/// Find a path that can be used to refer to a certain item. This can depend on
47/// *from where* you're referring to the item, hence the `from` parameter.
48pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
49 let _p = ra_prof::profile("find_path");
50 find_path_inner(db, item, from, MAX_PATH_LEN)
51}
52
53fn find_path_inner(
54 db: &dyn DefDatabase, 53 db: &dyn DefDatabase,
55 item: ItemInNs, 54 item: ItemInNs,
56 from: ModuleId, 55 from: ModuleId,
@@ -141,8 +140,7 @@ fn find_path_inner(
141 let mut best_path = None; 140 let mut best_path = None;
142 let mut best_path_len = max_len; 141 let mut best_path_len = max_len;
143 for (module_id, name) in importable_locations { 142 for (module_id, name) in importable_locations {
144 let mut path = match find_path_inner( 143 let mut path = match db.find_path_inner(
145 db,
146 ItemInNs::Types(ModuleDefId::ModuleId(module_id)), 144 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
147 from, 145 from,
148 best_path_len - 1, 146 best_path_len - 1,
@@ -165,17 +163,19 @@ fn find_path_inner(
165 163
166fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { 164fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
167 if old_path.starts_with_std() && new_path.can_start_with_std() { 165 if old_path.starts_with_std() && new_path.can_start_with_std() {
168 tested_by!(prefer_std_paths);
169 if prefer_no_std { 166 if prefer_no_std {
167 mark::hit!(prefer_no_std_paths);
170 new_path 168 new_path
171 } else { 169 } else {
170 mark::hit!(prefer_std_paths);
172 old_path 171 old_path
173 } 172 }
174 } else if new_path.starts_with_std() && old_path.can_start_with_std() { 173 } else if new_path.starts_with_std() && old_path.can_start_with_std() {
175 tested_by!(prefer_std_paths);
176 if prefer_no_std { 174 if prefer_no_std {
175 mark::hit!(prefer_no_std_paths);
177 old_path 176 old_path
178 } else { 177 } else {
178 mark::hit!(prefer_std_paths);
179 new_path 179 new_path
180 } 180 }
181 } else if new_path.len() < old_path.len() { 181 } else if new_path.len() < old_path.len() {
@@ -215,11 +215,12 @@ fn find_importable_locations(
215/// 215///
216/// Note that the crate doesn't need to be the one in which the item is defined; 216/// Note that the crate doesn't need to be the one in which the item is defined;
217/// it might be re-exported in other crates. 217/// it might be re-exported in other crates.
218pub(crate) fn importable_locations_in_crate( 218pub(crate) fn importable_locations_of_query(
219 db: &dyn DefDatabase, 219 db: &dyn DefDatabase,
220 item: ItemInNs, 220 item: ItemInNs,
221 krate: CrateId, 221 krate: CrateId,
222) -> Arc<[(ModuleId, Name, Visibility)]> { 222) -> Arc<[(ModuleId, Name, Visibility)]> {
223 let _p = profile("importable_locations_of_query");
223 let def_map = db.crate_def_map(krate); 224 let def_map = db.crate_def_map(krate);
224 let mut result = Vec::new(); 225 let mut result = Vec::new();
225 for (local_id, data) in def_map.modules.iter() { 226 for (local_id, data) in def_map.modules.iter() {
@@ -251,12 +252,14 @@ pub(crate) fn importable_locations_in_crate(
251 252
252#[cfg(test)] 253#[cfg(test)]
253mod tests { 254mod tests {
254 use super::*;
255 use crate::test_db::TestDB;
256 use hir_expand::hygiene::Hygiene; 255 use hir_expand::hygiene::Hygiene;
257 use ra_db::fixture::WithFixture; 256 use ra_db::fixture::WithFixture;
258 use ra_syntax::ast::AstNode; 257 use ra_syntax::ast::AstNode;
259 use test_utils::covers; 258 use test_utils::mark;
259
260 use crate::test_db::TestDB;
261
262 use super::*;
260 263
261 /// `code` needs to contain a cursor marker; checks that `find_path` for the 264 /// `code` needs to contain a cursor marker; checks that `find_path` for the
262 /// item the `path` refers to returns that same path when called from the 265 /// item the `path` refers to returns that same path when called from the
@@ -511,7 +514,7 @@ mod tests {
511 514
512 #[test] 515 #[test]
513 fn prefer_std_paths_over_alloc() { 516 fn prefer_std_paths_over_alloc() {
514 covers!(prefer_std_paths); 517 mark::check!(prefer_std_paths);
515 let code = r#" 518 let code = r#"
516 //- /main.rs crate:main deps:alloc,std 519 //- /main.rs crate:main deps:alloc,std
517 <|> 520 <|>
@@ -530,51 +533,50 @@ mod tests {
530 } 533 }
531 534
532 #[test] 535 #[test]
533 fn prefer_alloc_paths_over_std() { 536 fn prefer_core_paths_over_std() {
534 covers!(prefer_std_paths); 537 mark::check!(prefer_no_std_paths);
535 let code = r#" 538 let code = r#"
536 //- /main.rs crate:main deps:alloc,std 539 //- /main.rs crate:main deps:core,std
537 #![no_std] 540 #![no_std]
538 541
539 <|> 542 <|>
540 543
541 //- /std.rs crate:std deps:alloc 544 //- /std.rs crate:std deps:core
542 545
543 pub mod sync { 546 pub mod fmt {
544 pub use alloc::sync::Arc; 547 pub use core::fmt::Error;
545 } 548 }
546 549
547 //- /zzz.rs crate:alloc 550 //- /zzz.rs crate:core
548 551
549 pub mod sync { 552 pub mod fmt {
550 pub struct Arc; 553 pub struct Error;
551 } 554 }
552 "#; 555 "#;
553 check_found_path(code, "alloc::sync::Arc"); 556 check_found_path(code, "core::fmt::Error");
554 } 557 }
555 558
556 #[test] 559 #[test]
557 fn prefer_core_paths_over_std() { 560 fn prefer_alloc_paths_over_std() {
558 covers!(prefer_std_paths);
559 let code = r#" 561 let code = r#"
560 //- /main.rs crate:main deps:core,std 562 //- /main.rs crate:main deps:alloc,std
561 #![no_std] 563 #![no_std]
562 564
563 <|> 565 <|>
564 566
565 //- /std.rs crate:std deps:core 567 //- /std.rs crate:std deps:alloc
566 568
567 pub mod fmt { 569 pub mod sync {
568 pub use core::fmt::Error; 570 pub use alloc::sync::Arc;
569 } 571 }
570 572
571 //- /zzz.rs crate:core 573 //- /zzz.rs crate:alloc
572 574
573 pub mod fmt { 575 pub mod sync {
574 pub struct Error; 576 pub struct Arc;
575 } 577 }
576 "#; 578 "#;
577 check_found_path(code, "core::fmt::Error"); 579 check_found_path(code, "alloc::sync::Arc");
578 } 580 }
579 581
580 #[test] 582 #[test]
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_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index 518772e8a..5325a2760 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -46,8 +46,6 @@ pub mod find_path;
46 46
47#[cfg(test)] 47#[cfg(test)]
48mod test_db; 48mod test_db;
49#[cfg(test)]
50mod marks;
51 49
52use std::hash::Hash; 50use std::hash::Hash;
53 51
diff --git a/crates/ra_hir_def/src/marks.rs b/crates/ra_hir_def/src/marks.rs
deleted file mode 100644
index daa49d5f1..000000000
--- a/crates/ra_hir_def/src/marks.rs
+++ /dev/null
@@ -1,17 +0,0 @@
1//! See test_utils/src/marks.rs
2
3test_utils::marks!(
4 bogus_paths
5 name_res_works_for_broken_modules
6 can_import_enum_variant
7 glob_enum
8 glob_enum_group
9 glob_across_crates
10 std_prelude
11 macro_rules_from_other_crates_are_visible_with_macro_use
12 prelude_is_macro_use
13 macro_dollar_crate_self
14 macro_dollar_crate_other
15 infer_resolve_while_let
16 prefer_std_paths
17);
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index db994122a..353a31ad4 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -14,7 +14,7 @@ use ra_cfg::CfgOptions;
14use ra_db::{CrateId, FileId, ProcMacroId}; 14use ra_db::{CrateId, FileId, ProcMacroId};
15use ra_syntax::ast; 15use ra_syntax::ast;
16use rustc_hash::FxHashMap; 16use rustc_hash::FxHashMap;
17use test_utils::tested_by; 17use test_utils::mark;
18 18
19use crate::{ 19use crate::{
20 attr::Attrs, 20 attr::Attrs,
@@ -302,7 +302,7 @@ impl DefCollector<'_> {
302 ); 302 );
303 303
304 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { 304 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
305 tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use); 305 mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
306 self.import_all_macros_exported(current_module_id, m.krate); 306 self.import_all_macros_exported(current_module_id, m.krate);
307 } 307 }
308 } 308 }
@@ -412,10 +412,10 @@ impl DefCollector<'_> {
412 match def.take_types() { 412 match def.take_types() {
413 Some(ModuleDefId::ModuleId(m)) => { 413 Some(ModuleDefId::ModuleId(m)) => {
414 if import.is_prelude { 414 if import.is_prelude {
415 tested_by!(std_prelude); 415 mark::hit!(std_prelude);
416 self.def_map.prelude = Some(m); 416 self.def_map.prelude = Some(m);
417 } else if m.krate != self.def_map.krate { 417 } else if m.krate != self.def_map.krate {
418 tested_by!(glob_across_crates); 418 mark::hit!(glob_across_crates);
419 // glob import from other crate => we can just import everything once 419 // glob import from other crate => we can just import everything once
420 let item_map = self.db.crate_def_map(m.krate); 420 let item_map = self.db.crate_def_map(m.krate);
421 let scope = &item_map[m.local_id].scope; 421 let scope = &item_map[m.local_id].scope;
@@ -461,7 +461,7 @@ impl DefCollector<'_> {
461 } 461 }
462 } 462 }
463 Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { 463 Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
464 tested_by!(glob_enum); 464 mark::hit!(glob_enum);
465 // glob import from enum => just import all the variants 465 // glob import from enum => just import all the variants
466 466
467 // XXX: urgh, so this works by accident! Here, we look at 467 // XXX: urgh, so this works by accident! Here, we look at
@@ -510,7 +510,7 @@ impl DefCollector<'_> {
510 510
511 self.update(module_id, &[(name, def)], vis); 511 self.update(module_id, &[(name, def)], vis);
512 } 512 }
513 None => tested_by!(bogus_paths), 513 None => mark::hit!(bogus_paths),
514 } 514 }
515 } 515 }
516 } 516 }
@@ -683,7 +683,7 @@ impl ModCollector<'_, '_> {
683 // Prelude module is always considered to be `#[macro_use]`. 683 // Prelude module is always considered to be `#[macro_use]`.
684 if let Some(prelude_module) = self.def_collector.def_map.prelude { 684 if let Some(prelude_module) = self.def_collector.def_map.prelude {
685 if prelude_module.krate != self.def_collector.def_map.krate { 685 if prelude_module.krate != self.def_collector.def_map.krate {
686 tested_by!(prelude_is_macro_use); 686 mark::hit!(prelude_is_macro_use);
687 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); 687 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
688 } 688 }
689 } 689 }
diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs
index 35a0a0c98..19692e70c 100644
--- a/crates/ra_hir_def/src/nameres/path_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/path_resolution.rs
@@ -14,7 +14,7 @@ use std::iter::successors;
14 14
15use hir_expand::name::Name; 15use hir_expand::name::Name;
16use ra_db::Edition; 16use ra_db::Edition;
17use test_utils::tested_by; 17use test_utils::mark;
18 18
19use crate::{ 19use crate::{
20 db::DefDatabase, 20 db::DefDatabase,
@@ -108,7 +108,7 @@ impl CrateDefMap {
108 let mut curr_per_ns: PerNs = match path.kind { 108 let mut curr_per_ns: PerNs = match path.kind {
109 PathKind::DollarCrate(krate) => { 109 PathKind::DollarCrate(krate) => {
110 if krate == self.krate { 110 if krate == self.krate {
111 tested_by!(macro_dollar_crate_self); 111 mark::hit!(macro_dollar_crate_self);
112 PerNs::types( 112 PerNs::types(
113 ModuleId { krate: self.krate, local_id: self.root }.into(), 113 ModuleId { krate: self.krate, local_id: self.root }.into(),
114 Visibility::Public, 114 Visibility::Public,
@@ -116,7 +116,7 @@ impl CrateDefMap {
116 } else { 116 } else {
117 let def_map = db.crate_def_map(krate); 117 let def_map = db.crate_def_map(krate);
118 let module = ModuleId { krate, local_id: def_map.root }; 118 let module = ModuleId { krate, local_id: def_map.root };
119 tested_by!(macro_dollar_crate_other); 119 mark::hit!(macro_dollar_crate_other);
120 PerNs::types(module.into(), Visibility::Public) 120 PerNs::types(module.into(), Visibility::Public)
121 } 121 }
122 } 122 }
@@ -221,7 +221,7 @@ impl CrateDefMap {
221 } 221 }
222 ModuleDefId::AdtId(AdtId::EnumId(e)) => { 222 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
223 // enum variant 223 // enum variant
224 tested_by!(can_import_enum_variant); 224 mark::hit!(can_import_enum_variant);
225 let enum_data = db.enum_data(e); 225 let enum_data = db.enum_data(e);
226 match enum_data.variant(&segment) { 226 match enum_data.variant(&segment) {
227 Some(local_id) => { 227 Some(local_id) => {
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
index f2716a295..4e628b14d 100644
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -18,7 +18,7 @@ use ra_syntax::{
18 ast::{self, AttrsOwner, NameOwner, VisibilityOwner}, 18 ast::{self, AttrsOwner, NameOwner, VisibilityOwner},
19 AstNode, 19 AstNode,
20}; 20};
21use test_utils::tested_by; 21use test_utils::mark;
22 22
23use crate::{ 23use crate::{
24 attr::Attrs, 24 attr::Attrs,
@@ -346,7 +346,7 @@ impl RawItemsCollector {
346 self.push_item(current_module, attrs, RawItemKind::Module(item)); 346 self.push_item(current_module, attrs, RawItemKind::Module(item));
347 return; 347 return;
348 } 348 }
349 tested_by!(name_res_works_for_broken_modules); 349 mark::hit!(name_res_works_for_broken_modules);
350 } 350 }
351 351
352 fn add_use_item(&mut self, current_module: Option<Idx<ModuleData>>, use_item: ast::UseItem) { 352 fn add_use_item(&mut self, current_module: Option<Idx<ModuleData>>, use_item: ast::UseItem) {
diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs
index 1b66c1aac..05cd0297d 100644
--- a/crates/ra_hir_def/src/nameres/tests.rs
+++ b/crates/ra_hir_def/src/nameres/tests.rs
@@ -8,7 +8,7 @@ use std::sync::Arc;
8 8
9use insta::assert_snapshot; 9use insta::assert_snapshot;
10use ra_db::{fixture::WithFixture, SourceDatabase}; 10use ra_db::{fixture::WithFixture, SourceDatabase};
11use test_utils::covers; 11use test_utils::mark;
12 12
13use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; 13use crate::{db::DefDatabase, nameres::*, test_db::TestDB};
14 14
@@ -132,7 +132,7 @@ fn crate_def_map_fn_mod_same_name() {
132 132
133#[test] 133#[test]
134fn bogus_paths() { 134fn bogus_paths() {
135 covers!(bogus_paths); 135 mark::check!(bogus_paths);
136 let map = def_map( 136 let map = def_map(
137 " 137 "
138 //- /lib.rs 138 //- /lib.rs
@@ -247,7 +247,7 @@ fn re_exports() {
247 247
248#[test] 248#[test]
249fn std_prelude() { 249fn std_prelude() {
250 covers!(std_prelude); 250 mark::check!(std_prelude);
251 let map = def_map( 251 let map = def_map(
252 " 252 "
253 //- /main.rs crate:main deps:test_crate 253 //- /main.rs crate:main deps:test_crate
@@ -271,7 +271,7 @@ fn std_prelude() {
271 271
272#[test] 272#[test]
273fn can_import_enum_variant() { 273fn can_import_enum_variant() {
274 covers!(can_import_enum_variant); 274 mark::check!(can_import_enum_variant);
275 let map = def_map( 275 let map = def_map(
276 " 276 "
277 //- /lib.rs 277 //- /lib.rs
diff --git a/crates/ra_hir_def/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs
index ee8df3a26..2b12c0daa 100644
--- a/crates/ra_hir_def/src/nameres/tests/globs.rs
+++ b/crates/ra_hir_def/src/nameres/tests/globs.rs
@@ -152,7 +152,7 @@ fn glob_privacy_2() {
152 152
153#[test] 153#[test]
154fn glob_across_crates() { 154fn glob_across_crates() {
155 covers!(glob_across_crates); 155 mark::check!(glob_across_crates);
156 let map = def_map( 156 let map = def_map(
157 r" 157 r"
158 //- /main.rs crate:main deps:test_crate 158 //- /main.rs crate:main deps:test_crate
@@ -171,7 +171,6 @@ fn glob_across_crates() {
171 171
172#[test] 172#[test]
173fn glob_privacy_across_crates() { 173fn glob_privacy_across_crates() {
174 covers!(glob_across_crates);
175 let map = def_map( 174 let map = def_map(
176 r" 175 r"
177 //- /main.rs crate:main deps:test_crate 176 //- /main.rs crate:main deps:test_crate
@@ -191,7 +190,7 @@ fn glob_privacy_across_crates() {
191 190
192#[test] 191#[test]
193fn glob_enum() { 192fn glob_enum() {
194 covers!(glob_enum); 193 mark::check!(glob_enum);
195 let map = def_map( 194 let map = def_map(
196 " 195 "
197 //- /lib.rs 196 //- /lib.rs
@@ -212,7 +211,7 @@ fn glob_enum() {
212 211
213#[test] 212#[test]
214fn glob_enum_group() { 213fn glob_enum_group() {
215 covers!(glob_enum_group); 214 mark::check!(glob_enum_group);
216 let map = def_map( 215 let map = def_map(
217 r" 216 r"
218 //- /lib.rs 217 //- /lib.rs
diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs
index 40289e3ca..84480d9f6 100644
--- a/crates/ra_hir_def/src/nameres/tests/macros.rs
+++ b/crates/ra_hir_def/src/nameres/tests/macros.rs
@@ -212,7 +212,7 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
212 212
213#[test] 213#[test]
214fn macro_rules_from_other_crates_are_visible_with_macro_use() { 214fn macro_rules_from_other_crates_are_visible_with_macro_use() {
215 covers!(macro_rules_from_other_crates_are_visible_with_macro_use); 215 mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use);
216 let map = def_map( 216 let map = def_map(
217 " 217 "
218 //- /main.rs crate:main deps:foo 218 //- /main.rs crate:main deps:foo
@@ -262,7 +262,7 @@ fn macro_rules_from_other_crates_are_visible_with_macro_use() {
262 262
263#[test] 263#[test]
264fn prelude_is_macro_use() { 264fn prelude_is_macro_use() {
265 covers!(prelude_is_macro_use); 265 mark::check!(prelude_is_macro_use);
266 let map = def_map( 266 let map = def_map(
267 " 267 "
268 //- /main.rs crate:main deps:foo 268 //- /main.rs crate:main deps:foo
@@ -544,8 +544,7 @@ fn path_qualified_macros() {
544 544
545#[test] 545#[test]
546fn macro_dollar_crate_is_correct_in_item() { 546fn macro_dollar_crate_is_correct_in_item() {
547 covers!(macro_dollar_crate_self); 547 mark::check!(macro_dollar_crate_self);
548 covers!(macro_dollar_crate_other);
549 let map = def_map( 548 let map = def_map(
550 " 549 "
551 //- /main.rs crate:main deps:foo 550 //- /main.rs crate:main deps:foo
@@ -603,7 +602,7 @@ fn macro_dollar_crate_is_correct_in_item() {
603 602
604#[test] 603#[test]
605fn macro_dollar_crate_is_correct_in_indirect_deps() { 604fn macro_dollar_crate_is_correct_in_indirect_deps() {
606 covers!(macro_dollar_crate_other); 605 mark::check!(macro_dollar_crate_other);
607 // From std 606 // From std
608 let map = def_map( 607 let map = def_map(
609 r#" 608 r#"
diff --git a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
index 37fcdfb8c..b43b294ca 100644
--- a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
@@ -2,7 +2,7 @@ use super::*;
2 2
3#[test] 3#[test]
4fn name_res_works_for_broken_modules() { 4fn name_res_works_for_broken_modules() {
5 covers!(name_res_works_for_broken_modules); 5 mark::check!(name_res_works_for_broken_modules);
6 let map = def_map( 6 let map = def_map(
7 r" 7 r"
8 //- /lib.rs 8 //- /lib.rs
diff --git a/crates/ra_hir_def/src/path/lower/lower_use.rs b/crates/ra_hir_def/src/path/lower/lower_use.rs
index 5b6854b0f..7cc655487 100644
--- a/crates/ra_hir_def/src/path/lower/lower_use.rs
+++ b/crates/ra_hir_def/src/path/lower/lower_use.rs
@@ -6,7 +6,7 @@ use std::iter;
6use either::Either; 6use either::Either;
7use hir_expand::{hygiene::Hygiene, name::AsName}; 7use hir_expand::{hygiene::Hygiene, name::AsName};
8use ra_syntax::ast::{self, NameOwner}; 8use ra_syntax::ast::{self, NameOwner};
9use test_utils::tested_by; 9use test_utils::mark;
10 10
11use crate::path::{ImportAlias, ModPath, PathKind}; 11use crate::path::{ImportAlias, ModPath, PathKind};
12 12
@@ -54,7 +54,7 @@ pub(crate) fn lower_use_tree(
54 // FIXME: report errors somewhere 54 // FIXME: report errors somewhere
55 // We get here if we do 55 // We get here if we do
56 } else if is_glob { 56 } else if is_glob {
57 tested_by!(glob_enum_group); 57 mark::hit!(glob_enum_group);
58 if let Some(prefix) = prefix { 58 if let Some(prefix) = prefix {
59 cb(prefix, &tree, is_glob, None) 59 cb(prefix, &tree, is_glob, None)
60 } 60 }
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/_match.rs b/crates/ra_hir_ty/src/_match.rs
index 149f65042..3e6e1e333 100644
--- a/crates/ra_hir_ty/src/_match.rs
+++ b/crates/ra_hir_ty/src/_match.rs
@@ -1946,6 +1946,23 @@ mod tests {
1946 1946
1947 check_no_diagnostic(content); 1947 check_no_diagnostic(content);
1948 } 1948 }
1949
1950 #[test]
1951 fn expr_diverges_missing_arm() {
1952 let content = r"
1953 enum Either {
1954 A,
1955 B,
1956 }
1957 fn test_fn() {
1958 match loop {} {
1959 Either::A => (),
1960 }
1961 }
1962 ";
1963
1964 check_no_diagnostic(content);
1965 }
1949} 1966}
1950 1967
1951#[cfg(test)] 1968#[cfg(test)]
@@ -1998,26 +2015,6 @@ mod false_negatives {
1998 } 2015 }
1999 2016
2000 #[test] 2017 #[test]
2001 fn expr_diverges_missing_arm() {
2002 let content = r"
2003 enum Either {
2004 A,
2005 B,
2006 }
2007 fn test_fn() {
2008 match loop {} {
2009 Either::A => (),
2010 }
2011 }
2012 ";
2013
2014 // This is a false negative.
2015 // Even though the match expression diverges, rustc fails
2016 // to compile here since `Either::B` is missing.
2017 check_no_diagnostic(content);
2018 }
2019
2020 #[test]
2021 fn expr_loop_missing_arm() { 2018 fn expr_loop_missing_arm() {
2022 let content = r" 2019 let content = r"
2023 enum Either { 2020 enum Either {
@@ -2035,7 +2032,7 @@ mod false_negatives {
2035 // We currently infer the type of `loop { break Foo::A }` to `!`, which 2032 // We currently infer the type of `loop { break Foo::A }` to `!`, which
2036 // causes us to skip the diagnostic since `Either::A` doesn't type check 2033 // causes us to skip the diagnostic since `Either::A` doesn't type check
2037 // with `!`. 2034 // with `!`.
2038 check_no_diagnostic(content); 2035 check_diagnostic(content);
2039 } 2036 }
2040 2037
2041 #[test] 2038 #[test]
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/infer.rs b/crates/ra_hir_ty/src/infer.rs
index 2876cb141..957d6e0b5 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -218,6 +218,7 @@ struct InferenceContext<'a> {
218#[derive(Clone, Debug)] 218#[derive(Clone, Debug)]
219struct BreakableContext { 219struct BreakableContext {
220 pub may_break: bool, 220 pub may_break: bool,
221 pub break_ty: Ty,
221} 222}
222 223
223impl<'a> InferenceContext<'a> { 224impl<'a> InferenceContext<'a> {
diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs
index 173ec59ed..2ee9adb16 100644
--- a/crates/ra_hir_ty/src/infer/coerce.rs
+++ b/crates/ra_hir_ty/src/infer/coerce.rs
@@ -5,7 +5,7 @@
5//! See: https://doc.rust-lang.org/nomicon/coercions.html 5//! See: https://doc.rust-lang.org/nomicon/coercions.html
6 6
7use hir_def::{lang_item::LangItemTarget, type_ref::Mutability}; 7use hir_def::{lang_item::LangItemTarget, type_ref::Mutability};
8use test_utils::tested_by; 8use test_utils::mark;
9 9
10use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty, TypeCtor}; 10use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty, TypeCtor};
11 11
@@ -34,7 +34,7 @@ impl<'a> InferenceContext<'a> {
34 ty1.clone() 34 ty1.clone()
35 } else { 35 } else {
36 if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) { 36 if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) {
37 tested_by!(coerce_fn_reification); 37 mark::hit!(coerce_fn_reification);
38 // Special case: two function types. Try to coerce both to 38 // Special case: two function types. Try to coerce both to
39 // pointers to have a chance at getting a match. See 39 // pointers to have a chance at getting a match. See
40 // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 40 // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
@@ -44,7 +44,7 @@ impl<'a> InferenceContext<'a> {
44 let ptr_ty2 = Ty::fn_ptr(sig2); 44 let ptr_ty2 = Ty::fn_ptr(sig2);
45 self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) 45 self.coerce_merge_branch(&ptr_ty1, &ptr_ty2)
46 } else { 46 } else {
47 tested_by!(coerce_merge_fail_fallback); 47 mark::hit!(coerce_merge_fail_fallback);
48 // For incompatible types, we use the latter one as result 48 // For incompatible types, we use the latter one as result
49 // to be better recovery for `if` without `else`. 49 // to be better recovery for `if` without `else`.
50 ty2.clone() 50 ty2.clone()
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 0b67d216a..b28724f0e 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -93,22 +93,25 @@ impl<'a> InferenceContext<'a> {
93 Ty::Unknown 93 Ty::Unknown
94 } 94 }
95 Expr::Loop { body } => { 95 Expr::Loop { body } => {
96 self.breakables.push(BreakableContext { may_break: false }); 96 self.breakables.push(BreakableContext {
97 may_break: false,
98 break_ty: self.table.new_type_var(),
99 });
97 self.infer_expr(*body, &Expectation::has_type(Ty::unit())); 100 self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
98 101
99 let ctxt = self.breakables.pop().expect("breakable stack broken"); 102 let ctxt = self.breakables.pop().expect("breakable stack broken");
100 if ctxt.may_break { 103 if ctxt.may_break {
101 self.diverges = Diverges::Maybe; 104 self.diverges = Diverges::Maybe;
102 } 105 }
103 // FIXME handle break with value 106
104 if ctxt.may_break { 107 if ctxt.may_break {
105 Ty::unit() 108 ctxt.break_ty
106 } else { 109 } else {
107 Ty::simple(TypeCtor::Never) 110 Ty::simple(TypeCtor::Never)
108 } 111 }
109 } 112 }
110 Expr::While { condition, body } => { 113 Expr::While { condition, body } => {
111 self.breakables.push(BreakableContext { may_break: false }); 114 self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
112 // while let is desugared to a match loop, so this is always simple while 115 // while let is desugared to a match loop, so this is always simple while
113 self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); 116 self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
114 self.infer_expr(*body, &Expectation::has_type(Ty::unit())); 117 self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
@@ -120,7 +123,7 @@ impl<'a> InferenceContext<'a> {
120 Expr::For { iterable, body, pat } => { 123 Expr::For { iterable, body, pat } => {
121 let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); 124 let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
122 125
123 self.breakables.push(BreakableContext { may_break: false }); 126 self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
124 let pat_ty = 127 let pat_ty =
125 self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); 128 self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
126 129
@@ -229,17 +232,29 @@ impl<'a> InferenceContext<'a> {
229 } 232 }
230 Expr::Continue => Ty::simple(TypeCtor::Never), 233 Expr::Continue => Ty::simple(TypeCtor::Never),
231 Expr::Break { expr } => { 234 Expr::Break { expr } => {
232 if let Some(expr) = expr { 235 let val_ty = if let Some(expr) = expr {
233 // FIXME handle break with value 236 self.infer_expr(*expr, &Expectation::none())
234 self.infer_expr(*expr, &Expectation::none()); 237 } else {
235 } 238 Ty::unit()
239 };
240
241 let last_ty = if let Some(ctxt) = self.breakables.last() {
242 ctxt.break_ty.clone()
243 } else {
244 Ty::Unknown
245 };
246
247 let merged_type = self.coerce_merge_branch(&last_ty, &val_ty);
248
236 if let Some(ctxt) = self.breakables.last_mut() { 249 if let Some(ctxt) = self.breakables.last_mut() {
250 ctxt.break_ty = merged_type;
237 ctxt.may_break = true; 251 ctxt.may_break = true;
238 } else { 252 } else {
239 self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { 253 self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
240 expr: tgt_expr, 254 expr: tgt_expr,
241 }); 255 });
242 } 256 }
257
243 Ty::simple(TypeCtor::Never) 258 Ty::simple(TypeCtor::Never)
244 } 259 }
245 Expr::Return { expr } => { 260 Expr::Return { expr } => {
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs
index 54ec870df..4006f595d 100644
--- a/crates/ra_hir_ty/src/infer/pat.rs
+++ b/crates/ra_hir_ty/src/infer/pat.rs
@@ -10,7 +10,7 @@ use hir_def::{
10 FieldId, 10 FieldId,
11}; 11};
12use hir_expand::name::Name; 12use hir_expand::name::Name;
13use test_utils::tested_by; 13use test_utils::mark;
14 14
15use super::{BindingMode, Expectation, InferenceContext}; 15use super::{BindingMode, Expectation, InferenceContext};
16use crate::{utils::variant_data, Substs, Ty, TypeCtor}; 16use crate::{utils::variant_data, Substs, Ty, TypeCtor};
@@ -111,7 +111,7 @@ impl<'a> InferenceContext<'a> {
111 } 111 }
112 } 112 }
113 } else if let Pat::Ref { .. } = &body[pat] { 113 } else if let Pat::Ref { .. } = &body[pat] {
114 tested_by!(match_ergonomics_ref); 114 mark::hit!(match_ergonomics_ref);
115 // When you encounter a `&pat` pattern, reset to Move. 115 // When you encounter a `&pat` pattern, reset to Move.
116 // This is so that `w` is by value: `let (_, &w) = &(1, &2);` 116 // This is so that `w` is by value: `let (_, &w) = &(1, &2);`
117 default_bm = BindingMode::Move; 117 default_bm = BindingMode::Move;
diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs
index ab0bc8b70..269495ca0 100644
--- a/crates/ra_hir_ty/src/infer/unify.rs
+++ b/crates/ra_hir_ty/src/infer/unify.rs
@@ -4,7 +4,7 @@ use std::borrow::Cow;
4 4
5use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; 5use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue};
6 6
7use test_utils::tested_by; 7use test_utils::mark;
8 8
9use super::{InferenceContext, Obligation}; 9use super::{InferenceContext, Obligation};
10use crate::{ 10use crate::{
@@ -313,7 +313,7 @@ impl InferenceTable {
313 // more than once 313 // more than once
314 for i in 0..3 { 314 for i in 0..3 {
315 if i > 0 { 315 if i > 0 {
316 tested_by!(type_var_resolves_to_int_var); 316 mark::hit!(type_var_resolves_to_int_var);
317 } 317 }
318 match &*ty { 318 match &*ty {
319 Ty::Infer(tv) => { 319 Ty::Infer(tv) => {
@@ -342,7 +342,7 @@ impl InferenceTable {
342 Ty::Infer(tv) => { 342 Ty::Infer(tv) => {
343 let inner = tv.to_inner(); 343 let inner = tv.to_inner();
344 if tv_stack.contains(&inner) { 344 if tv_stack.contains(&inner) {
345 tested_by!(type_var_cycles_resolve_as_possible); 345 mark::hit!(type_var_cycles_resolve_as_possible);
346 // recursive type 346 // recursive type
347 return tv.fallback_value(); 347 return tv.fallback_value();
348 } 348 }
@@ -369,7 +369,7 @@ impl InferenceTable {
369 Ty::Infer(tv) => { 369 Ty::Infer(tv) => {
370 let inner = tv.to_inner(); 370 let inner = tv.to_inner();
371 if tv_stack.contains(&inner) { 371 if tv_stack.contains(&inner) {
372 tested_by!(type_var_cycles_resolve_completely); 372 mark::hit!(type_var_cycles_resolve_completely);
373 // recursive type 373 // recursive type
374 return tv.fallback_value(); 374 return tv.fallback_value();
375 } 375 }
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index daea02f88..93cb45a64 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -42,7 +42,6 @@ pub mod expr;
42mod tests; 42mod tests;
43#[cfg(test)] 43#[cfg(test)]
44mod test_db; 44mod test_db;
45mod marks;
46mod _match; 45mod _match;
47 46
48use std::ops::Deref; 47use std::ops::Deref;
@@ -156,10 +155,16 @@ pub enum TypeCtor {
156/// 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
157/// we have different kinds of ADTs, primitive types and special type 156/// we have different kinds of ADTs, primitive types and special type
158/// constructors like tuples and function pointers. 157/// constructors like tuples and function pointers.
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
160pub struct TypeCtorId(salsa::InternId); 159pub struct TypeCtorId(salsa::InternId);
161impl_intern_key!(TypeCtorId); 160impl_intern_key!(TypeCtorId);
162 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
163impl TypeCtor { 168impl TypeCtor {
164 pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize { 169 pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize {
165 match self { 170 match self {
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index 9ad6dbe07..35ac86a46 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -812,7 +812,7 @@ impl TraitEnvironment {
812 // add `Self: Trait<T1, T2, ...>` to the environment in trait 812 // add `Self: Trait<T1, T2, ...>` to the environment in trait
813 // function default implementations (and hypothetical code 813 // function default implementations (and hypothetical code
814 // inside consts or type aliases) 814 // inside consts or type aliases)
815 test_utils::tested_by!(trait_self_implements_self); 815 test_utils::mark::hit!(trait_self_implements_self);
816 let substs = Substs::type_params(db, trait_id); 816 let substs = Substs::type_params(db, trait_id);
817 let trait_ref = TraitRef { trait_: trait_id, substs }; 817 let trait_ref = TraitRef { trait_: trait_id, substs };
818 let pred = GenericPredicate::Implemented(trait_ref); 818 let pred = GenericPredicate::Implemented(trait_ref);
diff --git a/crates/ra_hir_ty/src/marks.rs b/crates/ra_hir_ty/src/marks.rs
deleted file mode 100644
index a39740143..000000000
--- a/crates/ra_hir_ty/src/marks.rs
+++ /dev/null
@@ -1,12 +0,0 @@
1//! See test_utils/src/marks.rs
2
3test_utils::marks!(
4 type_var_cycles_resolve_completely
5 type_var_cycles_resolve_as_possible
6 type_var_resolves_to_int_var
7 impl_self_type_match_without_receiver
8 match_ergonomics_ref
9 coerce_merge_fail_fallback
10 coerce_fn_reification
11 trait_self_implements_self
12);
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index 0851e16a8..e19628fdf 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -469,7 +469,7 @@ fn iterate_inherent_methods<T>(
469 // already happens in `is_valid_candidate` above; if not, we 469 // already happens in `is_valid_candidate` above; if not, we
470 // check it here 470 // check it here
471 if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { 471 if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() {
472 test_utils::tested_by!(impl_self_type_match_without_receiver); 472 test_utils::mark::hit!(impl_self_type_match_without_receiver);
473 continue; 473 continue;
474 } 474 }
475 if let Some(result) = callback(&self_ty.value, item) { 475 if let Some(result) = callback(&self_ty.value, item) {
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs
index 6dc4b2cd1..2cc4f4bf9 100644
--- a/crates/ra_hir_ty/src/tests/coercion.rs
+++ b/crates/ra_hir_ty/src/tests/coercion.rs
@@ -1,6 +1,6 @@
1use super::infer_with_mismatches; 1use super::infer_with_mismatches;
2use insta::assert_snapshot; 2use insta::assert_snapshot;
3use test_utils::covers; 3use test_utils::mark;
4 4
5// Infer with some common definitions and impls. 5// Infer with some common definitions and impls.
6fn infer(source: &str) -> String { 6fn infer(source: &str) -> String {
@@ -339,7 +339,7 @@ fn test(i: i32) {
339 339
340#[test] 340#[test]
341fn coerce_merge_one_by_one1() { 341fn coerce_merge_one_by_one1() {
342 covers!(coerce_merge_fail_fallback); 342 mark::check!(coerce_merge_fail_fallback);
343 343
344 assert_snapshot!( 344 assert_snapshot!(
345 infer(r#" 345 infer(r#"
@@ -547,7 +547,7 @@ fn test() {
547 547
548#[test] 548#[test]
549fn coerce_fn_items_in_match_arms() { 549fn coerce_fn_items_in_match_arms() {
550 covers!(coerce_fn_reification); 550 mark::check!(coerce_fn_reification);
551 assert_snapshot!( 551 assert_snapshot!(
552 infer_with_mismatches(r#" 552 infer_with_mismatches(r#"
553fn foo1(x: u32) -> isize { 1 } 553fn foo1(x: u32) -> isize { 1 }
diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs
index 9c2c9e1d2..558a70022 100644
--- a/crates/ra_hir_ty/src/tests/method_resolution.rs
+++ b/crates/ra_hir_ty/src/tests/method_resolution.rs
@@ -984,7 +984,7 @@ fn test() { S2.into()<|>; }
984 984
985#[test] 985#[test]
986fn method_resolution_overloaded_method() { 986fn method_resolution_overloaded_method() {
987 test_utils::covers!(impl_self_type_match_without_receiver); 987 test_utils::mark::check!(impl_self_type_match_without_receiver);
988 let t = type_at( 988 let t = type_at(
989 r#" 989 r#"
990//- main.rs 990//- main.rs
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs
index d83ff5e0e..0c5f972a2 100644
--- a/crates/ra_hir_ty/src/tests/patterns.rs
+++ b/crates/ra_hir_ty/src/tests/patterns.rs
@@ -1,5 +1,5 @@
1use insta::assert_snapshot; 1use insta::assert_snapshot;
2use test_utils::covers; 2use test_utils::mark;
3 3
4use super::{infer, infer_with_mismatches}; 4use super::{infer, infer_with_mismatches};
5 5
@@ -197,7 +197,7 @@ fn test() {
197 197
198#[test] 198#[test]
199fn infer_pattern_match_ergonomics_ref() { 199fn infer_pattern_match_ergonomics_ref() {
200 covers!(match_ergonomics_ref); 200 mark::check!(match_ergonomics_ref);
201 assert_snapshot!( 201 assert_snapshot!(
202 infer(r#" 202 infer(r#"
203fn test() { 203fn test() {
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index c2168222e..1f004bd63 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -1,9 +1,10 @@
1use insta::assert_snapshot; 1use insta::assert_snapshot;
2use test_utils::covers; 2use ra_db::fixture::WithFixture;
3use test_utils::mark;
3 4
4use super::infer;
5use crate::test_db::TestDB; 5use crate::test_db::TestDB;
6use ra_db::fixture::WithFixture; 6
7use super::infer;
7 8
8#[test] 9#[test]
9fn bug_484() { 10fn bug_484() {
@@ -89,8 +90,8 @@ fn quux() {
89 90
90#[test] 91#[test]
91fn recursive_vars() { 92fn recursive_vars() {
92 covers!(type_var_cycles_resolve_completely); 93 mark::check!(type_var_cycles_resolve_completely);
93 covers!(type_var_cycles_resolve_as_possible); 94 mark::check!(type_var_cycles_resolve_as_possible);
94 assert_snapshot!( 95 assert_snapshot!(
95 infer(r#" 96 infer(r#"
96fn test() { 97fn test() {
@@ -112,8 +113,6 @@ fn test() {
112 113
113#[test] 114#[test]
114fn recursive_vars_2() { 115fn recursive_vars_2() {
115 covers!(type_var_cycles_resolve_completely);
116 covers!(type_var_cycles_resolve_as_possible);
117 assert_snapshot!( 116 assert_snapshot!(
118 infer(r#" 117 infer(r#"
119fn test() { 118fn test() {
@@ -170,7 +169,7 @@ fn write() {
170 169
171#[test] 170#[test]
172fn infer_std_crash_2() { 171fn infer_std_crash_2() {
173 covers!(type_var_resolves_to_int_var); 172 mark::check!(type_var_resolves_to_int_var);
174 // caused "equating two type variables, ...", taken from std 173 // caused "equating two type variables, ...", taken from std
175 assert_snapshot!( 174 assert_snapshot!(
176 infer(r#" 175 infer(r#"
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 72122c070..fd2208af2 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -1860,3 +1860,66 @@ fn test() {
1860 "### 1860 "###
1861 ); 1861 );
1862} 1862}
1863
1864#[test]
1865fn infer_loop_break_with_val() {
1866 assert_snapshot!(
1867 infer(r#"
1868enum Option<T> { Some(T), None }
1869use Option::*;
1870
1871fn test() {
1872 let x = loop {
1873 if false {
1874 break None;
1875 }
1876
1877 break Some(true);
1878 };
1879}
1880"#),
1881 @r###"
1882 60..169 '{ ... }; }': ()
1883 70..71 'x': Option<bool>
1884 74..166 'loop {... }': Option<bool>
1885 79..166 '{ ... }': ()
1886 89..133 'if fal... }': ()
1887 92..97 'false': bool
1888 98..133 '{ ... }': ()
1889 112..122 'break None': !
1890 118..122 'None': Option<bool>
1891 143..159 'break ...(true)': !
1892 149..153 'Some': Some<bool>(bool) -> Option<bool>
1893 149..159 'Some(true)': Option<bool>
1894 154..158 'true': bool
1895 "###
1896 );
1897}
1898
1899#[test]
1900fn infer_loop_break_without_val() {
1901 assert_snapshot!(
1902 infer(r#"
1903enum Option<T> { Some(T), None }
1904use Option::*;
1905
1906fn test() {
1907 let x = loop {
1908 if false {
1909 break;
1910 }
1911 };
1912}
1913"#),
1914 @r###"
1915 60..137 '{ ... }; }': ()
1916 70..71 'x': ()
1917 74..134 'loop {... }': ()
1918 79..134 '{ ... }': ()
1919 89..128 'if fal... }': ()
1920 92..97 'false': bool
1921 98..128 '{ ... }': ()
1922 112..117 'break': !
1923 "###
1924 );
1925}
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index c49aacf98..0419bc751 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -1,10 +1,11 @@
1use insta::assert_snapshot; 1use insta::assert_snapshot;
2
3use ra_db::fixture::WithFixture; 2use ra_db::fixture::WithFixture;
3use test_utils::mark;
4 4
5use super::{infer, infer_with_mismatches, type_at, type_at_pos};
6use crate::test_db::TestDB; 5use crate::test_db::TestDB;
7 6
7use super::{infer, infer_with_mismatches, type_at, type_at_pos};
8
8#[test] 9#[test]
9fn infer_await() { 10fn infer_await() {
10 let (db, pos) = TestDB::with_position( 11 let (db, pos) = TestDB::with_position(
@@ -301,7 +302,7 @@ fn test() {
301 302
302#[test] 303#[test]
303fn trait_default_method_self_bound_implements_trait() { 304fn trait_default_method_self_bound_implements_trait() {
304 test_utils::covers!(trait_self_implements_self); 305 mark::check!(trait_self_implements_self);
305 assert_snapshot!( 306 assert_snapshot!(
306 infer(r#" 307 infer(r#"
307trait Trait { 308trait Trait {
@@ -324,7 +325,6 @@ trait Trait {
324 325
325#[test] 326#[test]
326fn trait_default_method_self_bound_implements_super_trait() { 327fn trait_default_method_self_bound_implements_super_trait() {
327 test_utils::covers!(trait_self_implements_self);
328 assert_snapshot!( 328 assert_snapshot!(
329 infer(r#" 329 infer(r#"
330trait SuperTrait { 330trait SuperTrait {
@@ -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_info.rs b/crates/ra_ide/src/call_info.rs
index 780a03c13..aa039e6fc 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -5,7 +5,7 @@ use ra_syntax::{
5 ast::{self, ArgListOwner}, 5 ast::{self, ArgListOwner},
6 match_ast, AstNode, SyntaxNode, SyntaxToken, 6 match_ast, AstNode, SyntaxNode, SyntaxToken,
7}; 7};
8use test_utils::tested_by; 8use test_utils::mark;
9 9
10use crate::{CallInfo, FilePosition, FunctionSignature}; 10use crate::{CallInfo, FilePosition, FunctionSignature};
11 11
@@ -84,7 +84,7 @@ fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Op
84 84
85 let arg_list_range = arg_list.syntax().text_range(); 85 let arg_list_range = arg_list.syntax().text_range();
86 if !arg_list_range.contains_inclusive(token.text_range().start()) { 86 if !arg_list_range.contains_inclusive(token.text_range().start()) {
87 tested_by!(call_info_bad_offset); 87 mark::hit!(call_info_bad_offset);
88 return None; 88 return None;
89 } 89 }
90 90
@@ -213,7 +213,7 @@ impl CallInfo {
213 213
214#[cfg(test)] 214#[cfg(test)]
215mod tests { 215mod tests {
216 use test_utils::covers; 216 use test_utils::mark;
217 217
218 use crate::mock_analysis::single_file_with_position; 218 use crate::mock_analysis::single_file_with_position;
219 219
@@ -529,7 +529,7 @@ By default this method stops actor's `Context`."#
529 529
530 #[test] 530 #[test]
531 fn call_info_bad_offset() { 531 fn call_info_bad_offset() {
532 covers!(call_info_bad_offset); 532 mark::check!(call_info_bad_offset);
533 let (analysis, position) = single_file_with_position( 533 let (analysis, position) = single_file_with_position(
534 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 534 r#"fn foo(x: u32, y: u32) -> u32 {x + y}
535 fn bar() { foo <|> (3, ); }"#, 535 fn bar() { foo <|> (3, ); }"#,
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index 8bdc43b1a..191300704 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -59,8 +59,8 @@ pub use crate::completion::{
59/// with ordering of completions (currently this is done by the client). 59/// with ordering of completions (currently this is done by the client).
60pub(crate) fn completions( 60pub(crate) fn completions(
61 db: &RootDatabase, 61 db: &RootDatabase,
62 position: FilePosition,
63 config: &CompletionConfig, 62 config: &CompletionConfig,
63 position: FilePosition,
64) -> Option<Completions> { 64) -> Option<Completions> {
65 let ctx = CompletionContext::new(db, position, config)?; 65 let ctx = CompletionContext::new(db, position, config)?;
66 66
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
index db7430454..02ac0166b 100644
--- a/crates/ra_ide/src/completion/complete_qualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_qualified_path.rs
@@ -3,7 +3,7 @@
3use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; 3use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
4use ra_syntax::AstNode; 4use ra_syntax::AstNode;
5use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
6use test_utils::tested_by; 6use test_utils::mark;
7 7
8use crate::completion::{CompletionContext, Completions}; 8use crate::completion::{CompletionContext, Completions};
9 9
@@ -40,7 +40,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
40 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { 40 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() {
41 if name_ref.syntax().text() == name.to_string().as_str() { 41 if name_ref.syntax().text() == name.to_string().as_str() {
42 // for `use self::foo<|>`, don't suggest `foo` as a completion 42 // for `use self::foo<|>`, don't suggest `foo` as a completion
43 tested_by!(dont_complete_current_use); 43 mark::hit!(dont_complete_current_use);
44 continue; 44 continue;
45 } 45 }
46 } 46 }
@@ -147,7 +147,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
147 147
148#[cfg(test)] 148#[cfg(test)]
149mod tests { 149mod tests {
150 use test_utils::covers; 150 use test_utils::mark;
151 151
152 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 152 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
153 use insta::assert_debug_snapshot; 153 use insta::assert_debug_snapshot;
@@ -158,7 +158,7 @@ mod tests {
158 158
159 #[test] 159 #[test]
160 fn dont_complete_current_use() { 160 fn dont_complete_current_use() {
161 covers!(dont_complete_current_use); 161 mark::check!(dont_complete_current_use);
162 let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference); 162 let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference);
163 assert!(completions.is_empty()); 163 assert!(completions.is_empty());
164 } 164 }
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index bd40af1cb..db791660a 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -1,7 +1,7 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use hir::ScopeDef; 3use hir::ScopeDef;
4use test_utils::tested_by; 4use test_utils::mark;
5 5
6use crate::completion::{CompletionContext, Completions}; 6use crate::completion::{CompletionContext, Completions};
7use hir::{Adt, ModuleDef, Type}; 7use hir::{Adt, ModuleDef, Type};
@@ -30,7 +30,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
30 if ctx.use_item_syntax.is_some() { 30 if ctx.use_item_syntax.is_some() {
31 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { 31 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
32 if name_ref.syntax().text() == name.to_string().as_str() { 32 if name_ref.syntax().text() == name.to_string().as_str() {
33 tested_by!(self_fulfilling_completion); 33 mark::hit!(self_fulfilling_completion);
34 return; 34 return;
35 } 35 }
36 } 36 }
@@ -66,7 +66,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
66#[cfg(test)] 66#[cfg(test)]
67mod tests { 67mod tests {
68 use insta::assert_debug_snapshot; 68 use insta::assert_debug_snapshot;
69 use test_utils::covers; 69 use test_utils::mark;
70 70
71 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 71 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
72 72
@@ -76,7 +76,7 @@ mod tests {
76 76
77 #[test] 77 #[test]
78 fn self_fulfilling_completion() { 78 fn self_fulfilling_completion() {
79 covers!(self_fulfilling_completion); 79 mark::check!(self_fulfilling_completion);
80 assert_debug_snapshot!( 80 assert_debug_snapshot!(
81 do_reference_completion( 81 do_reference_completion(
82 r#" 82 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/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 077cf9647..440ffa31d 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -3,7 +3,7 @@
3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; 3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
4use ra_syntax::ast::NameOwner; 4use ra_syntax::ast::NameOwner;
5use stdx::SepBy; 5use stdx::SepBy;
6use test_utils::tested_by; 6use test_utils::mark;
7 7
8use crate::{ 8use crate::{
9 completion::{ 9 completion::{
@@ -121,7 +121,7 @@ impl Completions {
121 _ => false, 121 _ => false,
122 }; 122 };
123 if has_non_default_type_params { 123 if has_non_default_type_params {
124 tested_by!(inserts_angle_brackets_for_generics); 124 mark::hit!(inserts_angle_brackets_for_generics);
125 completion_item = completion_item 125 completion_item = completion_item
126 .lookup_by(local_name.clone()) 126 .lookup_by(local_name.clone())
127 .label(format!("{}<…>", local_name)) 127 .label(format!("{}<…>", local_name))
@@ -176,7 +176,7 @@ impl Completions {
176 } 176 }
177 None if needs_bang => builder.insert_text(format!("{}!", name)), 177 None if needs_bang => builder.insert_text(format!("{}!", name)),
178 _ => { 178 _ => {
179 tested_by!(dont_insert_macro_call_parens_unncessary); 179 mark::hit!(dont_insert_macro_call_parens_unncessary);
180 builder.insert_text(name) 180 builder.insert_text(name)
181 } 181 }
182 }; 182 };
@@ -330,14 +330,14 @@ pub(crate) fn compute_score(
330 // FIXME: this should not fall back to string equality. 330 // FIXME: this should not fall back to string equality.
331 let ty = &ty.display(ctx.db).to_string(); 331 let ty = &ty.display(ctx.db).to_string();
332 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { 332 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
333 tested_by!(test_struct_field_completion_in_record_lit); 333 mark::hit!(test_struct_field_completion_in_record_lit);
334 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; 334 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
335 ( 335 (
336 struct_field.name(ctx.db).to_string(), 336 struct_field.name(ctx.db).to_string(),
337 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(), 337 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
338 ) 338 )
339 } else if let Some(active_parameter) = &ctx.active_parameter { 339 } else if let Some(active_parameter) = &ctx.active_parameter {
340 tested_by!(test_struct_field_completion_in_func_call); 340 mark::hit!(test_struct_field_completion_in_func_call);
341 (active_parameter.name.clone(), active_parameter.ty.clone()) 341 (active_parameter.name.clone(), active_parameter.ty.clone())
342 } else { 342 } else {
343 return None; 343 return None;
@@ -398,7 +398,7 @@ impl Builder {
398 None => return self, 398 None => return self,
399 }; 399 };
400 // If not an import, add parenthesis automatically. 400 // If not an import, add parenthesis automatically.
401 tested_by!(inserts_parens_for_function_calls); 401 mark::hit!(inserts_parens_for_function_calls);
402 402
403 let (snippet, label) = if params.is_empty() { 403 let (snippet, label) = if params.is_empty() {
404 (format!("{}()$0", name), format!("{}()", name)) 404 (format!("{}()$0", name), format!("{}()", name))
@@ -457,7 +457,7 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
457#[cfg(test)] 457#[cfg(test)]
458mod tests { 458mod tests {
459 use insta::assert_debug_snapshot; 459 use insta::assert_debug_snapshot;
460 use test_utils::covers; 460 use test_utils::mark;
461 461
462 use crate::completion::{ 462 use crate::completion::{
463 test_utils::{do_completion, do_completion_with_options}, 463 test_utils::{do_completion, do_completion_with_options},
@@ -607,7 +607,7 @@ mod tests {
607 607
608 #[test] 608 #[test]
609 fn inserts_parens_for_function_calls() { 609 fn inserts_parens_for_function_calls() {
610 covers!(inserts_parens_for_function_calls); 610 mark::check!(inserts_parens_for_function_calls);
611 assert_debug_snapshot!( 611 assert_debug_snapshot!(
612 do_reference_completion( 612 do_reference_completion(
613 r" 613 r"
@@ -992,7 +992,7 @@ mod tests {
992 992
993 #[test] 993 #[test]
994 fn inserts_angle_brackets_for_generics() { 994 fn inserts_angle_brackets_for_generics() {
995 covers!(inserts_angle_brackets_for_generics); 995 mark::check!(inserts_angle_brackets_for_generics);
996 assert_debug_snapshot!( 996 assert_debug_snapshot!(
997 do_reference_completion( 997 do_reference_completion(
998 r" 998 r"
@@ -1115,7 +1115,7 @@ mod tests {
1115 1115
1116 #[test] 1116 #[test]
1117 fn dont_insert_macro_call_parens_unncessary() { 1117 fn dont_insert_macro_call_parens_unncessary() {
1118 covers!(dont_insert_macro_call_parens_unncessary); 1118 mark::check!(dont_insert_macro_call_parens_unncessary);
1119 assert_debug_snapshot!( 1119 assert_debug_snapshot!(
1120 do_reference_completion( 1120 do_reference_completion(
1121 r" 1121 r"
@@ -1181,7 +1181,7 @@ mod tests {
1181 1181
1182 #[test] 1182 #[test]
1183 fn test_struct_field_completion_in_func_call() { 1183 fn test_struct_field_completion_in_func_call() {
1184 covers!(test_struct_field_completion_in_func_call); 1184 mark::check!(test_struct_field_completion_in_func_call);
1185 assert_debug_snapshot!( 1185 assert_debug_snapshot!(
1186 do_reference_completion( 1186 do_reference_completion(
1187 r" 1187 r"
@@ -1271,7 +1271,7 @@ mod tests {
1271 1271
1272 #[test] 1272 #[test]
1273 fn test_struct_field_completion_in_record_lit() { 1273 fn test_struct_field_completion_in_record_lit() {
1274 covers!(test_struct_field_completion_in_record_lit); 1274 mark::check!(test_struct_field_completion_in_record_lit);
1275 assert_debug_snapshot!( 1275 assert_debug_snapshot!(
1276 do_reference_completion( 1276 do_reference_completion(
1277 r" 1277 r"
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index eb90b5279..bf22452a2 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -20,7 +20,7 @@ pub(crate) fn do_completion_with_options(
20 } else { 20 } else {
21 single_file_with_position(code) 21 single_file_with_position(code)
22 }; 22 };
23 let completions = analysis.completions(position, options).unwrap().unwrap(); 23 let completions = analysis.completions(options, position).unwrap().unwrap();
24 let completion_items: Vec<CompletionItem> = completions.into(); 24 let completion_items: Vec<CompletionItem> = completions.into();
25 let mut kind_completions: Vec<CompletionItem> = 25 let mut kind_completions: Vec<CompletionItem> =
26 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); 26 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect();
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 87a0b80f1..3d83c0f71 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -21,7 +21,7 @@ use ra_syntax::{
21}; 21};
22use ra_text_edit::{TextEdit, TextEditBuilder}; 22use ra_text_edit::{TextEdit, TextEditBuilder};
23 23
24use crate::{Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; 24use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceChange, SourceFileEdit};
25 25
26#[derive(Debug, Copy, Clone)] 26#[derive(Debug, Copy, Clone)]
27pub enum Severity { 27pub enum Severity {
@@ -63,8 +63,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
63 .parent() 63 .parent()
64 .unwrap_or_else(|| RelativePath::new("")) 64 .unwrap_or_else(|| RelativePath::new(""))
65 .join(&d.candidate); 65 .join(&d.candidate);
66 let create_file = FileSystemEdit::CreateFile { source_root, path }; 66 let fix =
67 let fix = SourceChange::file_system_edit("Create module", create_file); 67 Fix::new("Create module", FileSystemEdit::CreateFile { source_root, path }.into());
68 res.borrow_mut().push(Diagnostic { 68 res.borrow_mut().push(Diagnostic {
69 range: sema.diagnostics_range(d).range, 69 range: sema.diagnostics_range(d).range,
70 message: d.message(), 70 message: d.message(),
@@ -88,14 +88,12 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
88 field_list = field_list.append_field(&field); 88 field_list = field_list.append_field(&field);
89 } 89 }
90 90
91 let mut builder = TextEditBuilder::default(); 91 let edit = {
92 algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); 92 let mut builder = TextEditBuilder::default();
93 93 algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder);
94 Some(SourceChange::source_file_edit_from( 94 builder.finish()
95 "Fill struct fields", 95 };
96 file_id, 96 Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
97 builder.finish(),
98 ))
99 }; 97 };
100 98
101 res.borrow_mut().push(Diagnostic { 99 res.borrow_mut().push(Diagnostic {
@@ -117,7 +115,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
117 let node = d.ast(db); 115 let node = d.ast(db);
118 let replacement = format!("Ok({})", node.syntax()); 116 let replacement = format!("Ok({})", node.syntax());
119 let edit = TextEdit::replace(node.syntax().text_range(), replacement); 117 let edit = TextEdit::replace(node.syntax().text_range(), replacement);
120 let fix = SourceChange::source_file_edit_from("Wrap with ok", file_id, edit); 118 let source_change = SourceChange::source_file_edit_from(file_id, edit);
119 let fix = Fix::new("Wrap with ok", source_change);
121 res.borrow_mut().push(Diagnostic { 120 res.borrow_mut().push(Diagnostic {
122 range: sema.diagnostics_range(d).range, 121 range: sema.diagnostics_range(d).range,
123 message: d.message(), 122 message: d.message(),
@@ -154,9 +153,9 @@ fn check_unnecessary_braces_in_use_statement(
154 range, 153 range,
155 message: "Unnecessary braces in use statement".to_string(), 154 message: "Unnecessary braces in use statement".to_string(),
156 severity: Severity::WeakWarning, 155 severity: Severity::WeakWarning,
157 fix: Some(SourceChange::source_file_edit( 156 fix: Some(Fix::new(
158 "Remove unnecessary braces", 157 "Remove unnecessary braces",
159 SourceFileEdit { file_id, edit }, 158 SourceFileEdit { file_id, edit }.into(),
160 )), 159 )),
161 }); 160 });
162 } 161 }
@@ -198,9 +197,9 @@ fn check_struct_shorthand_initialization(
198 range: record_field.syntax().text_range(), 197 range: record_field.syntax().text_range(),
199 message: "Shorthand struct initialization".to_string(), 198 message: "Shorthand struct initialization".to_string(),
200 severity: Severity::WeakWarning, 199 severity: Severity::WeakWarning,
201 fix: Some(SourceChange::source_file_edit( 200 fix: Some(Fix::new(
202 "Use struct shorthand initialization", 201 "Use struct shorthand initialization",
203 SourceFileEdit { file_id, edit }, 202 SourceFileEdit { file_id, edit }.into(),
204 )), 203 )),
205 }); 204 });
206 } 205 }
@@ -240,7 +239,7 @@ mod tests {
240 let diagnostic = 239 let diagnostic =
241 diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before)); 240 diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
242 let mut fix = diagnostic.fix.unwrap(); 241 let mut fix = diagnostic.fix.unwrap();
243 let edit = fix.source_file_edits.pop().unwrap().edit; 242 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
244 let actual = { 243 let actual = {
245 let mut actual = before.to_string(); 244 let mut actual = before.to_string();
246 edit.apply(&mut actual); 245 edit.apply(&mut actual);
@@ -258,7 +257,7 @@ mod tests {
258 let (analysis, file_position) = analysis_and_position(fixture); 257 let (analysis, file_position) = analysis_and_position(fixture);
259 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); 258 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap();
260 let mut fix = diagnostic.fix.unwrap(); 259 let mut fix = diagnostic.fix.unwrap();
261 let edit = fix.source_file_edits.pop().unwrap().edit; 260 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
262 let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); 261 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
263 let actual = { 262 let actual = {
264 let mut actual = target_file_contents.to_string(); 263 let mut actual = target_file_contents.to_string();
@@ -295,7 +294,7 @@ mod tests {
295 let (analysis, file_id) = single_file(before); 294 let (analysis, file_id) = single_file(before);
296 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); 295 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap();
297 let mut fix = diagnostic.fix.unwrap(); 296 let mut fix = diagnostic.fix.unwrap();
298 let edit = fix.source_file_edits.pop().unwrap().edit; 297 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
299 let actual = { 298 let actual = {
300 let mut actual = before.to_string(); 299 let mut actual = before.to_string();
301 edit.apply(&mut actual); 300 edit.apply(&mut actual);
@@ -616,22 +615,24 @@ mod tests {
616 Diagnostic { 615 Diagnostic {
617 message: "unresolved module", 616 message: "unresolved module",
618 range: 0..8, 617 range: 0..8,
618 severity: Error,
619 fix: Some( 619 fix: Some(
620 SourceChange { 620 Fix {
621 label: "Create module", 621 label: "Create module",
622 source_file_edits: [], 622 source_change: SourceChange {
623 file_system_edits: [ 623 source_file_edits: [],
624 CreateFile { 624 file_system_edits: [
625 source_root: SourceRootId( 625 CreateFile {
626 0, 626 source_root: SourceRootId(
627 ), 627 0,
628 path: "foo.rs", 628 ),
629 }, 629 path: "foo.rs",
630 ], 630 },
631 cursor_position: None, 631 ],
632 is_snippet: false,
633 },
632 }, 634 },
633 ), 635 ),
634 severity: Error,
635 }, 636 },
636 ] 637 ]
637 "###); 638 "###);
@@ -665,29 +666,31 @@ mod tests {
665 Diagnostic { 666 Diagnostic {
666 message: "Missing structure fields:\n- b", 667 message: "Missing structure fields:\n- b",
667 range: 224..233, 668 range: 224..233,
669 severity: Error,
668 fix: Some( 670 fix: Some(
669 SourceChange { 671 Fix {
670 label: "Fill struct fields", 672 label: "Fill struct fields",
671 source_file_edits: [ 673 source_change: SourceChange {
672 SourceFileEdit { 674 source_file_edits: [
673 file_id: FileId( 675 SourceFileEdit {
674 1, 676 file_id: FileId(
675 ), 677 1,
676 edit: TextEdit { 678 ),
677 indels: [ 679 edit: TextEdit {
678 Indel { 680 indels: [
679 insert: "{a:42, b: ()}", 681 Indel {
680 delete: 3..9, 682 insert: "{a:42, b: ()}",
681 }, 683 delete: 3..9,
682 ], 684 },
685 ],
686 },
683 }, 687 },
684 }, 688 ],
685 ], 689 file_system_edits: [],
686 file_system_edits: [], 690 is_snippet: false,
687 cursor_position: None, 691 },
688 }, 692 },
689 ), 693 ),
690 severity: Error,
691 }, 694 },
692 ] 695 ]
693 "###); 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/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index 150895abb..90e85d419 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -93,7 +93,7 @@ pub(crate) fn reference_definition(
93 93
94#[cfg(test)] 94#[cfg(test)]
95mod tests { 95mod tests {
96 use test_utils::{assert_eq_text, covers}; 96 use test_utils::assert_eq_text;
97 97
98 use crate::mock_analysis::analysis_and_position; 98 use crate::mock_analysis::analysis_and_position;
99 99
@@ -208,7 +208,6 @@ mod tests {
208 208
209 #[test] 209 #[test]
210 fn goto_def_for_macros() { 210 fn goto_def_for_macros() {
211 covers!(ra_ide_db::goto_def_for_macros);
212 check_goto( 211 check_goto(
213 " 212 "
214 //- /lib.rs 213 //- /lib.rs
@@ -225,7 +224,6 @@ mod tests {
225 224
226 #[test] 225 #[test]
227 fn goto_def_for_macros_from_other_crates() { 226 fn goto_def_for_macros_from_other_crates() {
228 covers!(ra_ide_db::goto_def_for_macros);
229 check_goto( 227 check_goto(
230 " 228 "
231 //- /lib.rs 229 //- /lib.rs
@@ -245,7 +243,6 @@ mod tests {
245 243
246 #[test] 244 #[test]
247 fn goto_def_for_use_alias() { 245 fn goto_def_for_use_alias() {
248 covers!(ra_ide_db::goto_def_for_use_alias);
249 check_goto( 246 check_goto(
250 " 247 "
251 //- /lib.rs 248 //- /lib.rs
@@ -370,7 +367,6 @@ mod tests {
370 367
371 #[test] 368 #[test]
372 fn goto_def_for_methods() { 369 fn goto_def_for_methods() {
373 covers!(ra_ide_db::goto_def_for_methods);
374 check_goto( 370 check_goto(
375 " 371 "
376 //- /lib.rs 372 //- /lib.rs
@@ -390,7 +386,6 @@ mod tests {
390 386
391 #[test] 387 #[test]
392 fn goto_def_for_fields() { 388 fn goto_def_for_fields() {
393 covers!(ra_ide_db::goto_def_for_fields);
394 check_goto( 389 check_goto(
395 r" 390 r"
396 //- /lib.rs 391 //- /lib.rs
@@ -409,7 +404,6 @@ mod tests {
409 404
410 #[test] 405 #[test]
411 fn goto_def_for_record_fields() { 406 fn goto_def_for_record_fields() {
412 covers!(ra_ide_db::goto_def_for_record_fields);
413 check_goto( 407 check_goto(
414 r" 408 r"
415 //- /lib.rs 409 //- /lib.rs
@@ -430,7 +424,6 @@ mod tests {
430 424
431 #[test] 425 #[test]
432 fn goto_def_for_record_pat_fields() { 426 fn goto_def_for_record_pat_fields() {
433 covers!(ra_ide_db::goto_def_for_record_field_pats);
434 check_goto( 427 check_goto(
435 r" 428 r"
436 //- /lib.rs 429 //- /lib.rs
@@ -873,7 +866,6 @@ mod tests {
873 866
874 #[test] 867 #[test]
875 fn goto_def_for_field_init_shorthand() { 868 fn goto_def_for_field_init_shorthand() {
876 covers!(ra_ide_db::goto_def_for_field_init_shorthand);
877 check_goto( 869 check_goto(
878 " 870 "
879 //- /lib.rs 871 //- /lib.rs
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 78149ddfc..5ac002d82 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -42,11 +42,6 @@ mod inlay_hints;
42mod expand_macro; 42mod expand_macro;
43mod ssr; 43mod ssr;
44 44
45#[cfg(test)]
46mod marks;
47#[cfg(test)]
48mod test_utils;
49
50use std::sync::Arc; 45use std::sync::Arc;
51 46
52use ra_cfg::CfgOptions; 47use ra_cfg::CfgOptions;
@@ -82,19 +77,19 @@ pub use crate::{
82}; 77};
83 78
84pub use hir::Documentation; 79pub use hir::Documentation;
85pub use ra_assists::AssistId; 80pub use ra_assists::{AssistConfig, AssistId};
86pub use ra_db::{ 81pub use ra_db::{
87 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, 82 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId,
88}; 83};
89pub use ra_ide_db::{ 84pub use ra_ide_db::{
90 change::{AnalysisChange, LibraryData}, 85 change::{AnalysisChange, LibraryData},
91 line_index::{LineCol, LineIndex}, 86 line_index::{LineCol, LineIndex},
92 line_index_utils::translate_offset_with_edit,
93 search::SearchScope, 87 search::SearchScope,
94 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 88 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
95 symbol_index::Query, 89 symbol_index::Query,
96 RootDatabase, 90 RootDatabase,
97}; 91};
92pub use ra_text_edit::{Indel, TextEdit};
98 93
99pub type Cancelable<T> = Result<T, Canceled>; 94pub type Cancelable<T> = Result<T, Canceled>;
100 95
@@ -102,8 +97,22 @@ pub type Cancelable<T> = Result<T, Canceled>;
102pub struct Diagnostic { 97pub struct Diagnostic {
103 pub message: String, 98 pub message: String,
104 pub range: TextRange, 99 pub range: TextRange,
105 pub fix: Option<SourceChange>,
106 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 }
107} 116}
108 117
109/// Info associated with a text range. 118/// Info associated with a text range.
@@ -291,14 +300,10 @@ impl Analysis {
291 300
292 /// 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
293 /// stuff like trailing commas. 302 /// stuff like trailing commas.
294 pub fn join_lines(&self, frange: FileRange) -> Cancelable<SourceChange> { 303 pub fn join_lines(&self, frange: FileRange) -> Cancelable<TextEdit> {
295 self.with_db(|db| { 304 self.with_db(|db| {
296 let parse = db.parse(frange.file_id); 305 let parse = db.parse(frange.file_id);
297 let file_edit = SourceFileEdit { 306 join_lines::join_lines(&parse.tree(), frange.range)
298 file_id: frange.file_id,
299 edit: join_lines::join_lines(&parse.tree(), frange.range),
300 };
301 SourceChange::source_file_edit("Join lines", file_edit)
302 }) 307 })
303 } 308 }
304 309
@@ -458,17 +463,17 @@ impl Analysis {
458 /// Computes completions at the given position. 463 /// Computes completions at the given position.
459 pub fn completions( 464 pub fn completions(
460 &self, 465 &self,
461 position: FilePosition,
462 config: &CompletionConfig, 466 config: &CompletionConfig,
467 position: FilePosition,
463 ) -> Cancelable<Option<Vec<CompletionItem>>> { 468 ) -> Cancelable<Option<Vec<CompletionItem>>> {
464 self.with_db(|db| completion::completions(db, position, config).map(Into::into)) 469 self.with_db(|db| completion::completions(db, config, position).map(Into::into))
465 } 470 }
466 471
467 /// Computes assists (aka code actions aka intentions) for the given 472 /// Computes assists (aka code actions aka intentions) for the given
468 /// position. 473 /// position.
469 pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<Assist>> { 474 pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable<Vec<Assist>> {
470 self.with_db(|db| { 475 self.with_db(|db| {
471 ra_assists::Assist::resolved(db, frange) 476 ra_assists::Assist::resolved(db, config, frange)
472 .into_iter() 477 .into_iter()
473 .map(|assist| Assist { 478 .map(|assist| Assist {
474 id: assist.assist.id, 479 id: assist.assist.id,
@@ -502,7 +507,7 @@ impl Analysis {
502 ) -> Cancelable<Result<SourceChange, SsrError>> { 507 ) -> Cancelable<Result<SourceChange, SsrError>> {
503 self.with_db(|db| { 508 self.with_db(|db| {
504 let edits = ssr::parse_search_replace(query, parse_only, db)?; 509 let edits = ssr::parse_search_replace(query, parse_only, db)?;
505 Ok(SourceChange::source_file_edits("Structural Search Replace", edits)) 510 Ok(SourceChange::source_file_edits(edits))
506 }) 511 })
507 } 512 }
508 513
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs
deleted file mode 100644
index 51ca4dde3..000000000
--- a/crates/ra_ide/src/marks.rs
+++ /dev/null
@@ -1,16 +0,0 @@
1//! See test_utils/src/marks.rs
2
3test_utils::marks!(
4 inserts_angle_brackets_for_generics
5 inserts_parens_for_function_calls
6 call_info_bad_offset
7 dont_complete_current_use
8 test_resolve_parent_module_on_module_decl
9 search_filters_by_range
10 dont_insert_macro_call_parens_unncessary
11 self_fulfilling_completion
12 test_struct_field_completion_in_func_call
13 test_struct_field_completion_in_record_lit
14 test_rename_struct_field_for_shorthand
15 test_rename_local_for_field_shorthand
16);
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs
index aaf4460df..a083fb1eb 100644
--- a/crates/ra_ide/src/parent_module.rs
+++ b/crates/ra_ide/src/parent_module.rs
@@ -7,7 +7,7 @@ use ra_syntax::{
7 algo::find_node_at_offset, 7 algo::find_node_at_offset,
8 ast::{self, AstNode}, 8 ast::{self, AstNode},
9}; 9};
10use test_utils::tested_by; 10use test_utils::mark;
11 11
12use crate::NavigationTarget; 12use crate::NavigationTarget;
13 13
@@ -25,7 +25,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
25 .item_list() 25 .item_list()
26 .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset)) 26 .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset))
27 { 27 {
28 tested_by!(test_resolve_parent_module_on_module_decl); 28 mark::hit!(test_resolve_parent_module_on_module_decl);
29 module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); 29 module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast);
30 } 30 }
31 } 31 }
@@ -57,7 +57,7 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
57mod tests { 57mod tests {
58 use ra_cfg::CfgOptions; 58 use ra_cfg::CfgOptions;
59 use ra_db::Env; 59 use ra_db::Env;
60 use test_utils::covers; 60 use test_utils::mark;
61 61
62 use crate::{ 62 use crate::{
63 mock_analysis::{analysis_and_position, MockAnalysis}, 63 mock_analysis::{analysis_and_position, MockAnalysis},
@@ -81,7 +81,7 @@ mod tests {
81 81
82 #[test] 82 #[test]
83 fn test_resolve_parent_module_on_module_decl() { 83 fn test_resolve_parent_module_on_module_decl() {
84 covers!(test_resolve_parent_module_on_module_decl); 84 mark::check!(test_resolve_parent_module_on_module_decl);
85 let (analysis, pos) = analysis_and_position( 85 let (analysis, pos) = analysis_and_position(
86 " 86 "
87 //- /lib.rs 87 //- /lib.rs
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index 074284b42..96444bf6a 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -190,8 +190,6 @@ fn get_struct_def_name_for_struct_literal_search(
190 190
191#[cfg(test)] 191#[cfg(test)]
192mod tests { 192mod tests {
193 use test_utils::covers;
194
195 use crate::{ 193 use crate::{
196 mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, 194 mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis},
197 Declaration, Reference, ReferenceSearchResult, SearchScope, 195 Declaration, Reference, ReferenceSearchResult, SearchScope,
@@ -301,7 +299,6 @@ mod tests {
301 299
302 #[test] 300 #[test]
303 fn search_filters_by_range() { 301 fn search_filters_by_range() {
304 covers!(ra_ide_db::search_filters_by_range);
305 let code = r#" 302 let code = r#"
306 fn foo() { 303 fn foo() {
307 let spam<|> = 92; 304 let spam<|> = 92;
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 410dae75c..28c6349b1 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -9,7 +9,7 @@ use ra_syntax::{
9}; 9};
10use ra_text_edit::TextEdit; 10use ra_text_edit::TextEdit;
11use std::convert::TryInto; 11use std::convert::TryInto;
12use test_utils::tested_by; 12use test_utils::mark;
13 13
14use crate::{ 14use crate::{
15 references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, 15 references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind,
@@ -57,13 +57,13 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
57 let file_id = reference.file_range.file_id; 57 let file_id = reference.file_range.file_id;
58 let range = match reference.kind { 58 let range = match reference.kind {
59 ReferenceKind::FieldShorthandForField => { 59 ReferenceKind::FieldShorthandForField => {
60 tested_by!(test_rename_struct_field_for_shorthand); 60 mark::hit!(test_rename_struct_field_for_shorthand);
61 replacement_text.push_str(new_name); 61 replacement_text.push_str(new_name);
62 replacement_text.push_str(": "); 62 replacement_text.push_str(": ");
63 TextRange::new(reference.file_range.range.start(), reference.file_range.range.start()) 63 TextRange::new(reference.file_range.range.start(), reference.file_range.range.start())
64 } 64 }
65 ReferenceKind::FieldShorthandForLocal => { 65 ReferenceKind::FieldShorthandForLocal => {
66 tested_by!(test_rename_local_for_field_shorthand); 66 mark::hit!(test_rename_local_for_field_shorthand);
67 replacement_text.push_str(": "); 67 replacement_text.push_str(": ");
68 replacement_text.push_str(new_name); 68 replacement_text.push_str(new_name);
69 TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) 69 TextRange::new(reference.file_range.range.end(), reference.file_range.range.end())
@@ -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,14 +253,14 @@ 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)]
260mod tests { 260mod tests {
261 use insta::assert_debug_snapshot; 261 use insta::assert_debug_snapshot;
262 use ra_text_edit::TextEditBuilder; 262 use ra_text_edit::TextEditBuilder;
263 use test_utils::{assert_eq_text, covers}; 263 use test_utils::{assert_eq_text, mark};
264 264
265 use crate::{ 265 use crate::{
266 mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, 266 mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId,
@@ -492,7 +492,7 @@ mod tests {
492 492
493 #[test] 493 #[test]
494 fn test_rename_struct_field_for_shorthand() { 494 fn test_rename_struct_field_for_shorthand() {
495 covers!(test_rename_struct_field_for_shorthand); 495 mark::check!(test_rename_struct_field_for_shorthand);
496 test_rename( 496 test_rename(
497 r#" 497 r#"
498 struct Foo { 498 struct Foo {
@@ -522,7 +522,7 @@ mod tests {
522 522
523 #[test] 523 #[test]
524 fn test_rename_local_for_field_shorthand() { 524 fn test_rename_local_for_field_shorthand() {
525 covers!(test_rename_local_for_field_shorthand); 525 mark::check!(test_rename_local_for_field_shorthand);
526 test_rename( 526 test_rename(
527 r#" 527 r#"
528 struct Foo { 528 struct Foo {
@@ -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,7 @@ mod tests {
669 dst_path: "bar/foo2.rs", 668 dst_path: "bar/foo2.rs",
670 }, 669 },
671 ], 670 ],
672 cursor_position: None, 671 is_snippet: false,
673 }, 672 },
674 }, 673 },
675 ) 674 )
@@ -694,7 +693,6 @@ mod tests {
694 RangeInfo { 693 RangeInfo {
695 range: 4..7, 694 range: 4..7,
696 info: SourceChange { 695 info: SourceChange {
697 label: "Rename",
698 source_file_edits: [ 696 source_file_edits: [
699 SourceFileEdit { 697 SourceFileEdit {
700 file_id: FileId( 698 file_id: FileId(
@@ -721,7 +719,7 @@ mod tests {
721 dst_path: "foo2/mod.rs", 719 dst_path: "foo2/mod.rs",
722 }, 720 },
723 ], 721 ],
724 cursor_position: None, 722 is_snippet: false,
725 }, 723 },
726 }, 724 },
727 ) 725 )
@@ -777,7 +775,6 @@ mod tests {
777 RangeInfo { 775 RangeInfo {
778 range: 8..11, 776 range: 8..11,
779 info: SourceChange { 777 info: SourceChange {
780 label: "Rename",
781 source_file_edits: [ 778 source_file_edits: [
782 SourceFileEdit { 779 SourceFileEdit {
783 file_id: FileId( 780 file_id: FileId(
@@ -817,7 +814,7 @@ mod tests {
817 dst_path: "bar/foo2.rs", 814 dst_path: "bar/foo2.rs",
818 }, 815 },
819 ], 816 ],
820 cursor_position: None, 817 is_snippet: false,
821 }, 818 },
822 }, 819 },
823 ) 820 )
@@ -983,8 +980,8 @@ mod tests {
983 if let Some(change) = source_change { 980 if let Some(change) = source_change {
984 for edit in change.info.source_file_edits { 981 for edit in change.info.source_file_edits {
985 file_id = Some(edit.file_id); 982 file_id = Some(edit.file_id);
986 for indel in edit.edit.as_indels() { 983 for indel in edit.edit.into_iter() {
987 text_edit_builder.replace(indel.delete, indel.insert.clone()); 984 text_edit_builder.replace(indel.delete, indel.insert);
988 } 985 }
989 } 986 }
990 } 987 }
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index fa8a9d92c..131b8f307 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::Semantics; 3use hir::{AsAssocItem, Semantics};
4use itertools::Itertools; 4use itertools::Itertools;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
@@ -65,14 +65,36 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run
65 RunnableKind::Bin 65 RunnableKind::Bin
66 } else { 66 } else {
67 let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) { 67 let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) {
68 let path = module 68 let def = sema.to_def(&fn_def)?;
69 let impl_trait_name =
70 def.as_assoc_item(sema.db).and_then(|assoc_item| {
71 match assoc_item.container(sema.db) {
72 hir::AssocItemContainer::Trait(trait_item) => {
73 Some(trait_item.name(sema.db).to_string())
74 }
75 hir::AssocItemContainer::ImplDef(impl_def) => impl_def
76 .target_ty(sema.db)
77 .as_adt()
78 .map(|adt| adt.name(sema.db).to_string()),
79 }
80 });
81
82 let path_iter = module
69 .path_to_root(sema.db) 83 .path_to_root(sema.db)
70 .into_iter() 84 .into_iter()
71 .rev() 85 .rev()
72 .filter_map(|it| it.name(sema.db)) 86 .filter_map(|it| it.name(sema.db))
73 .map(|name| name.to_string()) 87 .map(|name| name.to_string());
74 .chain(std::iter::once(name_string)) 88
75 .join("::"); 89 let path = if let Some(impl_trait_name) = impl_trait_name {
90 path_iter
91 .chain(std::iter::once(impl_trait_name))
92 .chain(std::iter::once(name_string))
93 .join("::")
94 } else {
95 path_iter.chain(std::iter::once(name_string)).join("::")
96 };
97
76 TestId::Path(path) 98 TestId::Path(path)
77 } else { 99 } else {
78 TestId::Name(name_string) 100 TestId::Name(name_string)
@@ -238,6 +260,44 @@ mod tests {
238 } 260 }
239 261
240 #[test] 262 #[test]
263 fn test_runnables_doc_test_in_impl() {
264 let (analysis, pos) = analysis_and_position(
265 r#"
266 //- /lib.rs
267 <|> //empty
268 fn main() {}
269
270 struct Data;
271 impl Data {
272 /// ```
273 /// let x = 5;
274 /// ```
275 fn foo() {}
276 }
277 "#,
278 );
279 let runnables = analysis.runnables(pos.file_id).unwrap();
280 assert_debug_snapshot!(&runnables,
281 @r###"
282 [
283 Runnable {
284 range: 1..21,
285 kind: Bin,
286 },
287 Runnable {
288 range: 51..105,
289 kind: DocTest {
290 test_id: Path(
291 "Data::foo",
292 ),
293 },
294 },
295 ]
296 "###
297 );
298 }
299
300 #[test]
241 fn test_runnables_module() { 301 fn test_runnables_module() {
242 let (analysis, pos) = analysis_and_position( 302 let (analysis, pos) = analysis_and_position(
243 r#" 303 r#"
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 6f04f0be4..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,15 +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 cursor_position: None,
86 })
87} 79}
88 80
89/// 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.
90fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { 82fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
91 assert_eq!(file.syntax().text().char_at(offset), Some('.')); 83 assert_eq!(file.syntax().text().char_at(offset), Some('.'));
92 let whitespace = 84 let whitespace =
93 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)?;
@@ -108,15 +100,11 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange>
108 return None; 100 return None;
109 } 101 }
110 102
111 Some(SingleFileChange { 103 Some(TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent))
112 label: "reindent dot".to_string(),
113 edit: TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent),
114 cursor_position: Some(offset + target_indent_len - current_indent_len + TextSize::of('.')),
115 })
116} 104}
117 105
118/// 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() -> { ... }`
119fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { 107fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
120 let file_text = file.syntax().text(); 108 let file_text = file.syntax().text();
121 assert_eq!(file_text.char_at(offset), Some('>')); 109 assert_eq!(file_text.char_at(offset), Some('>'));
122 let after_arrow = offset + TextSize::of('>'); 110 let after_arrow = offset + TextSize::of('>');
@@ -127,11 +115,7 @@ fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChang
127 return None; 115 return None;
128 } 116 }
129 117
130 Some(SingleFileChange { 118 Some(TextEdit::insert(after_arrow, " ".to_string()))
131 label: "add space after return type".to_string(),
132 edit: TextEdit::insert(after_arrow, " ".to_string()),
133 cursor_position: Some(after_arrow),
134 })
135} 119}
136 120
137#[cfg(test)] 121#[cfg(test)]
@@ -140,29 +124,23 @@ mod tests {
140 124
141 use super::*; 125 use super::*;
142 126
143 fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> { 127 fn do_type_char(char_typed: char, before: &str) -> Option<String> {
144 let (offset, before) = extract_offset(before); 128 let (offset, before) = extract_offset(before);
145 let edit = TextEdit::insert(offset, char_typed.to_string()); 129 let edit = TextEdit::insert(offset, char_typed.to_string());
146 let mut before = before.to_string(); 130 let mut before = before.to_string();
147 edit.apply(&mut before); 131 edit.apply(&mut before);
148 let parse = SourceFile::parse(&before); 132 let parse = SourceFile::parse(&before);
149 on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { 133 on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| {
150 it.edit.apply(&mut before); 134 it.apply(&mut before);
151 (before.to_string(), it) 135 before.to_string()
152 }) 136 })
153 } 137 }
154 138
155 fn type_char(char_typed: char, before: &str, after: &str) { 139 fn type_char(char_typed: char, before: &str, after: &str) {
156 let (actual, file_change) = do_type_char(char_typed, before) 140 let actual = do_type_char(char_typed, before)
157 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); 141 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));
158 142
159 if after.contains("<|>") { 143 assert_eq_text!(after, &actual);
160 let (offset, after) = extract_offset(after);
161 assert_eq_text!(&after, &actual);
162 assert_eq!(file_change.cursor_position, Some(offset))
163 } else {
164 assert_eq_text!(after, &actual);
165 }
166 } 144 }
167 145
168 fn type_char_noop(char_typed: char, before: &str) { 146 fn type_char_noop(char_typed: char, before: &str) {
@@ -350,6 +328,6 @@ fn foo() {
350 328
351 #[test] 329 #[test]
352 fn adds_space_after_return_type() { 330 fn adds_space_after_return_type() {
353 type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -><|> { 92 }") 331 type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -> { 92 }")
354 } 332 }
355} 333}
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/defs.rs b/crates/ra_ide_db/src/defs.rs
index 60c11178e..8b06cbfc5 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -14,7 +14,6 @@ use ra_syntax::{
14 ast::{self, AstNode}, 14 ast::{self, AstNode},
15 match_ast, 15 match_ast,
16}; 16};
17use test_utils::tested_by;
18 17
19use crate::RootDatabase; 18use crate::RootDatabase;
20 19
@@ -118,7 +117,6 @@ fn classify_name_inner(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Opti
118 match_ast! { 117 match_ast! {
119 match parent { 118 match parent {
120 ast::Alias(it) => { 119 ast::Alias(it) => {
121 tested_by!(goto_def_for_use_alias; force);
122 let use_tree = it.syntax().parent().and_then(ast::UseTree::cast)?; 120 let use_tree = it.syntax().parent().and_then(ast::UseTree::cast)?;
123 let path = use_tree.path()?; 121 let path = use_tree.path()?;
124 let path_segment = path.segment()?; 122 let path_segment = path.segment()?;
@@ -203,6 +201,8 @@ impl NameRefClass {
203 } 201 }
204} 202}
205 203
204// Note: we don't have unit-tests for this rather important function.
205// It is primarily exercised via goto definition tests in `ra_ide`.
206pub fn classify_name_ref( 206pub fn classify_name_ref(
207 sema: &Semantics<RootDatabase>, 207 sema: &Semantics<RootDatabase>,
208 name_ref: &ast::NameRef, 208 name_ref: &ast::NameRef,
@@ -212,22 +212,18 @@ pub fn classify_name_ref(
212 let parent = name_ref.syntax().parent()?; 212 let parent = name_ref.syntax().parent()?;
213 213
214 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { 214 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
215 tested_by!(goto_def_for_methods; force);
216 if let Some(func) = sema.resolve_method_call(&method_call) { 215 if let Some(func) = sema.resolve_method_call(&method_call) {
217 return Some(NameRefClass::Definition(Definition::ModuleDef(func.into()))); 216 return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
218 } 217 }
219 } 218 }
220 219
221 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { 220 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
222 tested_by!(goto_def_for_fields; force);
223 if let Some(field) = sema.resolve_field(&field_expr) { 221 if let Some(field) = sema.resolve_field(&field_expr) {
224 return Some(NameRefClass::Definition(Definition::Field(field))); 222 return Some(NameRefClass::Definition(Definition::Field(field)));
225 } 223 }
226 } 224 }
227 225
228 if let Some(record_field) = ast::RecordField::for_field_name(name_ref) { 226 if let Some(record_field) = ast::RecordField::for_field_name(name_ref) {
229 tested_by!(goto_def_for_record_fields; force);
230 tested_by!(goto_def_for_field_init_shorthand; force);
231 if let Some((field, local)) = sema.resolve_record_field(&record_field) { 227 if let Some((field, local)) = sema.resolve_record_field(&record_field) {
232 let field = Definition::Field(field); 228 let field = Definition::Field(field);
233 let res = match local { 229 let res = match local {
@@ -239,7 +235,6 @@ pub fn classify_name_ref(
239 } 235 }
240 236
241 if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent.clone()) { 237 if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent.clone()) {
242 tested_by!(goto_def_for_record_field_pats; force);
243 if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { 238 if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
244 let field = Definition::Field(field); 239 let field = Definition::Field(field);
245 return Some(NameRefClass::Definition(field)); 240 return Some(NameRefClass::Definition(field));
@@ -247,7 +242,6 @@ pub fn classify_name_ref(
247 } 242 }
248 243
249 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { 244 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
250 tested_by!(goto_def_for_macros; force);
251 if let Some(macro_def) = sema.resolve_macro_call(&macro_call) { 245 if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
252 return Some(NameRefClass::Definition(Definition::Macro(macro_def))); 246 return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
253 } 247 }
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index 52fcd7b6f..1b74e6558 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -2,9 +2,7 @@
2//! 2//!
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 marks;
6pub mod line_index; 5pub mod line_index;
7pub mod line_index_utils;
8pub mod symbol_index; 6pub mod symbol_index;
9pub mod change; 7pub mod change;
10pub 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/marks.rs b/crates/ra_ide_db/src/marks.rs
deleted file mode 100644
index 386fe605c..000000000
--- a/crates/ra_ide_db/src/marks.rs
+++ /dev/null
@@ -1,12 +0,0 @@
1//! See test_utils/src/marks.rs
2
3test_utils::marks![
4 goto_def_for_macros
5 goto_def_for_use_alias
6 goto_def_for_methods
7 goto_def_for_fields
8 goto_def_for_record_fields
9 goto_def_for_field_init_shorthand
10 goto_def_for_record_field_pats
11 search_filters_by_range
12];
diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs
index b464959fc..589f44771 100644
--- a/crates/ra_ide_db/src/search.rs
+++ b/crates/ra_ide_db/src/search.rs
@@ -12,7 +12,6 @@ use ra_db::{FileId, FileRange, SourceDatabaseExt};
12use ra_prof::profile; 12use ra_prof::profile;
13use ra_syntax::{ast, match_ast, AstNode, TextRange, TextSize}; 13use ra_syntax::{ast, match_ast, AstNode, TextRange, TextSize};
14use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
15use test_utils::tested_by;
16 15
17use crate::{ 16use crate::{
18 defs::{classify_name_ref, Definition, NameRefClass}, 17 defs::{classify_name_ref, Definition, NameRefClass},
@@ -209,7 +208,6 @@ impl Definition {
209 for (idx, _) in text.match_indices(pat) { 208 for (idx, _) in text.match_indices(pat) {
210 let offset: TextSize = idx.try_into().unwrap(); 209 let offset: TextSize = idx.try_into().unwrap();
211 if !search_range.contains_inclusive(offset) { 210 if !search_range.contains_inclusive(offset) {
212 tested_by!(search_filters_by_range; force);
213 continue; 211 continue;
214 } 212 }
215 213
diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs
index af81a91a4..e713f4b7e 100644
--- a/crates/ra_ide_db/src/source_change.rs
+++ b/crates/ra_ide_db/src/source_change.rs
@@ -3,90 +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, TextSize}; 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>, 13 pub is_snippet: bool,
16} 14}
17 15
18impl SourceChange { 16impl SourceChange {
19 /// Creates a new SourceChange with the given label 17 /// Creates a new SourceChange with the given label
20 /// from the edits. 18 /// from the edits.
21 pub fn from_edits<L: Into<String>>( 19 pub fn from_edits(
22 label: L,
23 source_file_edits: Vec<SourceFileEdit>, 20 source_file_edits: Vec<SourceFileEdit>,
24 file_system_edits: Vec<FileSystemEdit>, 21 file_system_edits: Vec<FileSystemEdit>,
25 ) -> Self { 22 ) -> Self {
26 SourceChange { 23 SourceChange { source_file_edits, file_system_edits, is_snippet: false }
27 label: label.into(),
28 source_file_edits,
29 file_system_edits,
30 cursor_position: None,
31 }
32 } 24 }
33 25
34 /// Creates a new SourceChange with the given label, 26 /// Creates a new SourceChange with the given label,
35 /// containing only the given `SourceFileEdits`. 27 /// containing only the given `SourceFileEdits`.
36 pub fn source_file_edits<L: Into<String>>(label: L, edits: Vec<SourceFileEdit>) -> Self { 28 pub fn source_file_edits(edits: Vec<SourceFileEdit>) -> Self {
37 let label = label.into(); 29 SourceChange { source_file_edits: edits, file_system_edits: vec![], is_snippet: false }
38 assert!(label.starts_with(char::is_uppercase));
39 SourceChange {
40 label: label,
41 source_file_edits: edits,
42 file_system_edits: vec![],
43 cursor_position: None,
44 }
45 }
46
47 /// Creates a new SourceChange with the given label,
48 /// containing only the given `FileSystemEdits`.
49 pub(crate) fn file_system_edits<L: Into<String>>(label: L, edits: Vec<FileSystemEdit>) -> Self {
50 SourceChange {
51 label: label.into(),
52 source_file_edits: vec![],
53 file_system_edits: edits,
54 cursor_position: None,
55 }
56 }
57
58 /// Creates a new SourceChange with the given label,
59 /// containing only a single `SourceFileEdit`.
60 pub fn source_file_edit<L: Into<String>>(label: L, edit: SourceFileEdit) -> Self {
61 SourceChange::source_file_edits(label, vec![edit])
62 }
63
64 /// Creates a new SourceChange with the given label
65 /// from the given `FileId` and `TextEdit`
66 pub fn source_file_edit_from<L: Into<String>>(
67 label: L,
68 file_id: FileId,
69 edit: TextEdit,
70 ) -> Self {
71 SourceChange::source_file_edit(label, SourceFileEdit { file_id, edit })
72 } 30 }
73
74 /// Creates a new SourceChange with the given label 31 /// Creates a new SourceChange with the given label
75 /// from the given `FileId` and `TextEdit` 32 /// from the given `FileId` and `TextEdit`
76 pub fn file_system_edit<L: Into<String>>(label: L, edit: FileSystemEdit) -> Self { 33 pub fn source_file_edit_from(file_id: FileId, edit: TextEdit) -> Self {
77 SourceChange::file_system_edits(label, vec![edit]) 34 SourceFileEdit { file_id, edit }.into()
78 }
79
80 /// Sets the cursor position to the given `FilePosition`
81 pub fn with_cursor(mut self, cursor_position: FilePosition) -> Self {
82 self.cursor_position = Some(cursor_position);
83 self
84 }
85
86 /// Sets the cursor position to the given `FilePosition`
87 pub fn with_cursor_opt(mut self, cursor_position: Option<FilePosition>) -> Self {
88 self.cursor_position = cursor_position;
89 self
90 } 35 }
91} 36}
92 37
@@ -96,25 +41,28 @@ pub struct SourceFileEdit {
96 pub edit: TextEdit, 41 pub edit: TextEdit,
97} 42}
98 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
99#[derive(Debug, Clone)] 54#[derive(Debug, Clone)]
100pub enum FileSystemEdit { 55pub enum FileSystemEdit {
101 CreateFile { source_root: SourceRootId, path: RelativePathBuf }, 56 CreateFile { source_root: SourceRootId, path: RelativePathBuf },
102 MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf }, 57 MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf },
103} 58}
104 59
105pub struct SingleFileChange { 60impl From<FileSystemEdit> for SourceChange {
106 pub label: String, 61 fn from(edit: FileSystemEdit) -> SourceChange {
107 pub edit: TextEdit,
108 pub cursor_position: Option<TextSize>,
109}
110
111impl SingleFileChange {
112 pub fn into_source_change(self, file_id: FileId) -> SourceChange {
113 SourceChange { 62 SourceChange {
114 label: self.label, 63 source_file_edits: Vec::new(),
115 source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }], 64 file_system_edits: vec![edit],
116 file_system_edits: Vec::new(), 65 is_snippet: false,
117 cursor_position: self.cursor_position.map(|offset| FilePosition { file_id, offset }),
118 } 66 }
119 } 67 }
120} 68}
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/algo.rs b/crates/ra_syntax/src/algo.rs
index 2a8dac757..664894d1f 100644
--- a/crates/ra_syntax/src/algo.rs
+++ b/crates/ra_syntax/src/algo.rs
@@ -266,6 +266,15 @@ impl<'a> SyntaxRewriter<'a> {
266 let replacement = Replacement::Single(with.clone().into()); 266 let replacement = Replacement::Single(with.clone().into());
267 self.replacements.insert(what, replacement); 267 self.replacements.insert(what, replacement);
268 } 268 }
269 pub fn replace_with_many<T: Clone + Into<SyntaxElement>>(
270 &mut self,
271 what: &T,
272 with: Vec<SyntaxElement>,
273 ) {
274 let what = what.clone().into();
275 let replacement = Replacement::Many(with);
276 self.replacements.insert(what, replacement);
277 }
269 pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) { 278 pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) {
270 self.replace(what.syntax(), with.syntax()) 279 self.replace(what.syntax(), with.syntax())
271 } 280 }
@@ -302,31 +311,41 @@ impl<'a> SyntaxRewriter<'a> {
302 311
303 fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { 312 fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode {
304 // FIXME: this could be made much faster. 313 // FIXME: this could be made much faster.
305 let new_children = 314 let mut new_children = Vec::new();
306 node.children_with_tokens().flat_map(|it| self.rewrite_self(&it)).collect::<Vec<_>>(); 315 for child in node.children_with_tokens() {
316 self.rewrite_self(&mut new_children, &child);
317 }
307 with_children(node, new_children) 318 with_children(node, new_children)
308 } 319 }
309 320
310 fn rewrite_self( 321 fn rewrite_self(
311 &self, 322 &self,
323 acc: &mut Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>,
312 element: &SyntaxElement, 324 element: &SyntaxElement,
313 ) -> Option<NodeOrToken<rowan::GreenNode, rowan::GreenToken>> { 325 ) {
314 if let Some(replacement) = self.replacement(&element) { 326 if let Some(replacement) = self.replacement(&element) {
315 return match replacement { 327 match replacement {
316 Replacement::Single(NodeOrToken::Node(it)) => { 328 Replacement::Single(NodeOrToken::Node(it)) => {
317 Some(NodeOrToken::Node(it.green().clone())) 329 acc.push(NodeOrToken::Node(it.green().clone()))
318 } 330 }
319 Replacement::Single(NodeOrToken::Token(it)) => { 331 Replacement::Single(NodeOrToken::Token(it)) => {
320 Some(NodeOrToken::Token(it.green().clone())) 332 acc.push(NodeOrToken::Token(it.green().clone()))
321 } 333 }
322 Replacement::Delete => None, 334 Replacement::Many(replacements) => {
335 acc.extend(replacements.iter().map(|it| match it {
336 NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()),
337 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()),
338 }))
339 }
340 Replacement::Delete => (),
323 }; 341 };
342 return;
324 } 343 }
325 let res = match element { 344 let res = match element {
326 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), 345 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()),
327 NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), 346 NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()),
328 }; 347 };
329 Some(res) 348 acc.push(res)
330 } 349 }
331} 350}
332 351
@@ -341,6 +360,7 @@ impl ops::AddAssign for SyntaxRewriter<'_> {
341enum Replacement { 360enum Replacement {
342 Delete, 361 Delete,
343 Single(SyntaxElement), 362 Single(SyntaxElement),
363 Many(Vec<SyntaxElement>),
344} 364}
345 365
346fn with_children( 366fn with_children(
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index 24a1e1d91..29eb3fcb9 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -1,7 +1,10 @@
1//! This module contains functions for editing syntax trees. As the trees are 1//! This module contains functions for editing syntax trees. As the trees are
2//! immutable, all function here return a fresh copy of the tree, instead of 2//! immutable, all function here return a fresh copy of the tree, instead of
3//! doing an in-place modification. 3//! doing an in-place modification.
4use std::{iter, ops::RangeInclusive}; 4use std::{
5 fmt, iter,
6 ops::{self, RangeInclusive},
7};
5 8
6use arrayvec::ArrayVec; 9use arrayvec::ArrayVec;
7 10
@@ -437,6 +440,28 @@ impl From<u8> for IndentLevel {
437 } 440 }
438} 441}
439 442
443impl fmt::Display for IndentLevel {
444 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
445 let spaces = " ";
446 let buf;
447 let len = self.0 as usize * 4;
448 let indent = if len <= spaces.len() {
449 &spaces[..len]
450 } else {
451 buf = iter::repeat(' ').take(len).collect::<String>();
452 &buf
453 };
454 fmt::Display::fmt(indent, f)
455 }
456}
457
458impl ops::Add<u8> for IndentLevel {
459 type Output = IndentLevel;
460 fn add(self, rhs: u8) -> IndentLevel {
461 IndentLevel(self.0 + rhs)
462 }
463}
464
440impl IndentLevel { 465impl IndentLevel {
441 pub fn from_node(node: &SyntaxNode) -> IndentLevel { 466 pub fn from_node(node: &SyntaxNode) -> IndentLevel {
442 let first_token = match node.first_token() { 467 let first_token = match node.first_token() {
@@ -453,6 +478,14 @@ impl IndentLevel {
453 IndentLevel(0) 478 IndentLevel(0)
454 } 479 }
455 480
481 /// XXX: this intentionally doesn't change the indent of the very first token.
482 /// Ie, in something like
483 /// ```
484 /// fn foo() {
485 /// 92
486 /// }
487 /// ```
488 /// if you indent the block, the `{` token would stay put.
456 fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { 489 fn increase_indent(self, node: SyntaxNode) -> SyntaxNode {
457 let mut rewriter = SyntaxRewriter::default(); 490 let mut rewriter = SyntaxRewriter::default();
458 node.descendants_with_tokens() 491 node.descendants_with_tokens()
@@ -463,12 +496,7 @@ impl IndentLevel {
463 text.contains('\n') 496 text.contains('\n')
464 }) 497 })
465 .for_each(|ws| { 498 .for_each(|ws| {
466 let new_ws = make::tokens::whitespace(&format!( 499 let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self,));
467 "{}{:width$}",
468 ws.syntax().text(),
469 "",
470 width = self.0 as usize * 4
471 ));
472 rewriter.replace(ws.syntax(), &new_ws) 500 rewriter.replace(ws.syntax(), &new_ws)
473 }); 501 });
474 rewriter.rewrite(&node) 502 rewriter.rewrite(&node)
@@ -485,7 +513,7 @@ impl IndentLevel {
485 }) 513 })
486 .for_each(|ws| { 514 .for_each(|ws| {
487 let new_ws = make::tokens::whitespace( 515 let new_ws = make::tokens::whitespace(
488 &ws.syntax().text().replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"), 516 &ws.syntax().text().replace(&format!("\n{}", self), "\n"),
489 ); 517 );
490 rewriter.replace(ws.syntax(), &new_ws) 518 rewriter.replace(ws.syntax(), &new_ws)
491 }); 519 });
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index d0e960fb4..da0eb0926 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -1,5 +1,9 @@
1//! This module contains free-standing functions for creating AST fragments out 1//! This module contains free-standing functions for creating AST fragments out
2//! of smaller pieces. 2//! of smaller pieces.
3//!
4//! Note that all functions here intended to be stupid constructors, which just
5//! assemble a finish node from immediate children. If you want to do something
6//! smarter than that, it probably doesn't belong in this module.
3use itertools::Itertools; 7use itertools::Itertools;
4use stdx::format_to; 8use stdx::format_to;
5 9
@@ -95,6 +99,9 @@ pub fn expr_empty_block() -> ast::Expr {
95pub fn expr_unimplemented() -> ast::Expr { 99pub fn expr_unimplemented() -> ast::Expr {
96 expr_from_text("unimplemented!()") 100 expr_from_text("unimplemented!()")
97} 101}
102pub fn expr_unreachable() -> ast::Expr {
103 expr_from_text("unreachable!()")
104}
98pub fn expr_todo() -> ast::Expr { 105pub fn expr_todo() -> ast::Expr {
99 expr_from_text("todo!()") 106 expr_from_text("todo!()")
100} 107}
@@ -264,10 +271,6 @@ pub fn token(kind: SyntaxKind) -> SyntaxToken {
264 .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) 271 .unwrap_or_else(|| panic!("unhandled token: {:?}", kind))
265} 272}
266 273
267pub fn unreachable_macro_call() -> ast::MacroCall {
268 ast_from_text(&format!("unreachable!()"))
269}
270
271pub fn param(name: String, ty: String) -> ast::Param { 274pub fn param(name: String, ty: String) -> ast::Param {
272 ast_from_text(&format!("fn f({}: {}) {{ }}", name, ty)) 275 ast_from_text(&format!("fn f({}: {}) {{ }}", name, ty))
273} 276}
@@ -277,7 +280,12 @@ pub fn param_list(pats: impl IntoIterator<Item = ast::Param>) -> ast::ParamList
277 ast_from_text(&format!("fn f({}) {{ }}", args)) 280 ast_from_text(&format!("fn f({}) {{ }}", args))
278} 281}
279 282
283pub fn visibility_pub_crate() -> ast::Visibility {
284 ast_from_text("pub(crate) struct S")
285}
286
280pub fn fn_def( 287pub fn fn_def(
288 visibility: Option<ast::Visibility>,
281 fn_name: ast::Name, 289 fn_name: ast::Name,
282 type_params: Option<ast::TypeParamList>, 290 type_params: Option<ast::TypeParamList>,
283 params: ast::ParamList, 291 params: ast::ParamList,
@@ -285,21 +293,11 @@ pub fn fn_def(
285) -> ast::FnDef { 293) -> ast::FnDef {
286 let type_params = 294 let type_params =
287 if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() }; 295 if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() };
288 ast_from_text(&format!("fn {}{}{} {}", fn_name, type_params, params, body)) 296 let visibility = match visibility {
289} 297 None => String::new(),
290 298 Some(it) => format!("{} ", it),
291pub fn add_leading_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile { 299 };
292 let newlines = "\n".repeat(amount_of_newlines); 300 ast_from_text(&format!("{}fn {}{}{} {}", visibility, fn_name, type_params, params, body))
293 ast_from_text(&format!("{}{}", newlines, t.syntax()))
294}
295
296pub fn add_trailing_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile {
297 let newlines = "\n".repeat(amount_of_newlines);
298 ast_from_text(&format!("{}{}", t.syntax(), newlines))
299}
300
301pub fn add_pub_crate_modifier(fn_def: ast::FnDef) -> ast::FnDef {
302 ast_from_text(&format!("pub(crate) {}", fn_def))
303} 301}
304 302
305fn ast_from_text<N: AstNode>(text: &str) -> N { 303fn ast_from_text<N: AstNode>(text: &str) -> N {
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/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 6147ae207..b20efe98d 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -105,7 +105,7 @@ pub fn analysis_bench(
105 if is_completion { 105 if is_completion {
106 let options = CompletionConfig::default(); 106 let options = CompletionConfig::default();
107 let res = do_work(&mut host, file_id, |analysis| { 107 let res = do_work(&mut host, file_id, |analysis| {
108 analysis.completions(file_position, &options) 108 analysis.completions(&options, file_position)
109 }); 109 });
110 if verbosity.is_verbose() { 110 if verbosity.is_verbose() {
111 println!("\n{:#?}", res); 111 println!("\n{:#?}", res);
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index b5dc6f0fa..c0f7c2c0c 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -11,7 +11,7 @@ use std::{ffi::OsString, path::PathBuf};
11 11
12use lsp_types::ClientCapabilities; 12use lsp_types::ClientCapabilities;
13use ra_flycheck::FlycheckConfig; 13use ra_flycheck::FlycheckConfig;
14use ra_ide::{CompletionConfig, InlayHintsConfig}; 14use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig};
15use ra_project_model::CargoConfig; 15use ra_project_model::CargoConfig;
16use serde::Deserialize; 16use serde::Deserialize;
17 17
@@ -32,6 +32,7 @@ pub struct Config {
32 32
33 pub inlay_hints: InlayHintsConfig, 33 pub inlay_hints: InlayHintsConfig,
34 pub completion: CompletionConfig, 34 pub completion: CompletionConfig,
35 pub assist: AssistConfig,
35 pub call_info_full: bool, 36 pub call_info_full: bool,
36 pub lens: LensConfig, 37 pub lens: LensConfig,
37} 38}
@@ -101,6 +102,7 @@ pub struct ClientCapsConfig {
101 pub hierarchical_symbols: bool, 102 pub hierarchical_symbols: bool,
102 pub code_action_literals: bool, 103 pub code_action_literals: bool,
103 pub work_done_progress: bool, 104 pub work_done_progress: bool,
105 pub code_action_group: bool,
104} 106}
105 107
106impl Default for Config { 108impl Default for Config {
@@ -136,6 +138,7 @@ impl Default for Config {
136 add_call_argument_snippets: true, 138 add_call_argument_snippets: true,
137 ..CompletionConfig::default() 139 ..CompletionConfig::default()
138 }, 140 },
141 assist: AssistConfig::default(),
139 call_info_full: true, 142 call_info_full: true,
140 lens: LensConfig::default(), 143 lens: LensConfig::default(),
141 } 144 }
@@ -266,13 +269,12 @@ impl Config {
266 { 269 {
267 self.client_caps.hierarchical_symbols = value 270 self.client_caps.hierarchical_symbols = value
268 } 271 }
269 if let Some(value) = doc_caps 272 if let Some(value) =
270 .code_action 273 doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some())
271 .as_ref()
272 .and_then(|it| Some(it.code_action_literal_support.is_some()))
273 { 274 {
274 self.client_caps.code_action_literals = value; 275 self.client_caps.code_action_literals = value;
275 } 276 }
277
276 self.completion.allow_snippets(false); 278 self.completion.allow_snippets(false);
277 if let Some(completion) = &doc_caps.completion { 279 if let Some(completion) = &doc_caps.completion {
278 if let Some(completion_item) = &completion.completion_item { 280 if let Some(completion_item) = &completion.completion_item {
@@ -288,5 +290,16 @@ impl Config {
288 self.client_caps.work_done_progress = value; 290 self.client_caps.work_done_progress = value;
289 } 291 }
290 } 292 }
293
294 self.assist.allow_snippets(false);
295 if let Some(experimental) = &caps.experimental {
296 let snippet_text_edit =
297 experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true);
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
303 }
291 } 304 }
292} 305}
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 4bdd45a7d..25856c543 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -3,9 +3,11 @@ pub(crate) mod to_proto;
3 3
4use std::{collections::HashMap, sync::Arc}; 4use std::{collections::HashMap, sync::Arc};
5 5
6use lsp_types::{CodeActionOrCommand, Diagnostic, Range}; 6use lsp_types::{Diagnostic, Range};
7use ra_ide::FileId; 7use ra_ide::FileId;
8 8
9use crate::lsp_ext;
10
9pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>; 11pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>;
10 12
11#[derive(Debug, Default, Clone)] 13#[derive(Debug, Default, Clone)]
@@ -18,13 +20,13 @@ pub struct DiagnosticCollection {
18#[derive(Debug, Clone)] 20#[derive(Debug, Clone)]
19pub struct Fix { 21pub struct Fix {
20 pub range: Range, 22 pub range: Range,
21 pub action: CodeActionOrCommand, 23 pub action: lsp_ext::CodeAction,
22} 24}
23 25
24#[derive(Debug)] 26#[derive(Debug)]
25pub enum DiagnosticTask { 27pub enum DiagnosticTask {
26 ClearCheck, 28 ClearCheck,
27 AddCheck(FileId, Diagnostic, Vec<CodeActionOrCommand>), 29 AddCheck(FileId, Diagnostic, Vec<lsp_ext::CodeAction>),
28 SetNative(FileId, Vec<Diagnostic>), 30 SetNative(FileId, Vec<Diagnostic>),
29} 31}
30 32
@@ -38,7 +40,7 @@ impl DiagnosticCollection {
38 &mut self, 40 &mut self,
39 file_id: FileId, 41 file_id: FileId,
40 diagnostic: Diagnostic, 42 diagnostic: Diagnostic,
41 fixes: Vec<CodeActionOrCommand>, 43 fixes: Vec<lsp_ext::CodeAction>,
42 ) { 44 ) {
43 let diagnostics = self.check.entry(file_id).or_default(); 45 let diagnostics = self.check.entry(file_id).or_default();
44 for existing_diagnostic in diagnostics.iter() { 46 for existing_diagnostic in diagnostics.iter() {
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 076b3cf27..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,12 +65,13 @@ 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 ),
71 diagnostics: None, 72 command: None,
72 edit: Some( 73 edit: Some(
73 WorkspaceEdit { 74 SnippetWorkspaceEdit {
74 changes: Some( 75 changes: Some(
75 { 76 {
76 "file:///test/src/main.rs": [ 77 "file:///test/src/main.rs": [
@@ -106,8 +107,6 @@ expression: diag
106 document_changes: None, 107 document_changes: None,
107 }, 108 },
108 ), 109 ),
109 command: None,
110 is_preferred: None,
111 }, 110 },
112 ], 111 ],
113 }, 112 },
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 69138c15b..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,12 +50,13 @@ 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 ),
56 diagnostics: None, 57 command: None,
57 edit: Some( 58 edit: Some(
58 WorkspaceEdit { 59 SnippetWorkspaceEdit {
59 changes: Some( 60 changes: Some(
60 { 61 {
61 "file:///test/driver/subcommand/repl.rs": [ 62 "file:///test/driver/subcommand/repl.rs": [
@@ -78,8 +79,6 @@ expression: diag
78 document_changes: None, 79 document_changes: None,
79 }, 80 },
80 ), 81 ),
81 command: None,
82 is_preferred: None,
83 }, 82 },
84 ], 83 ],
85 }, 84 },
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index eabf4908f..a500d670a 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -7,13 +7,13 @@ use std::{
7}; 7};
8 8
9use lsp_types::{ 9use lsp_types::{
10 CodeAction, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, 10 Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location,
11 Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit, 11 NumberOrString, Position, Range, TextEdit, Url,
12}; 12};
13use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion}; 13use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion};
14use stdx::format_to; 14use stdx::format_to;
15 15
16use crate::Result; 16use crate::{lsp_ext, Result};
17 17
18/// Converts a Rust level string to a LSP severity 18/// Converts a Rust level string to a LSP severity
19fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> { 19fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> {
@@ -110,7 +110,7 @@ fn is_deprecated(rd: &ra_flycheck::Diagnostic) -> bool {
110 110
111enum MappedRustChildDiagnostic { 111enum MappedRustChildDiagnostic {
112 Related(DiagnosticRelatedInformation), 112 Related(DiagnosticRelatedInformation),
113 SuggestedFix(CodeAction), 113 SuggestedFix(lsp_ext::CodeAction),
114 MessageLine(String), 114 MessageLine(String),
115} 115}
116 116
@@ -143,13 +143,16 @@ fn map_rust_child_diagnostic(
143 message: rd.message.clone(), 143 message: rd.message.clone(),
144 }) 144 })
145 } else { 145 } else {
146 MappedRustChildDiagnostic::SuggestedFix(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 diagnostics: None, 150 edit: Some(lsp_ext::SnippetWorkspaceEdit {
150 edit: Some(WorkspaceEdit::new(edit_map)), 151 // FIXME: there's no good reason to use edit_map here....
152 changes: Some(edit_map),
153 document_changes: None,
154 }),
151 command: None, 155 command: None,
152 is_preferred: None,
153 }) 156 })
154 } 157 }
155} 158}
@@ -158,7 +161,7 @@ fn map_rust_child_diagnostic(
158pub(crate) struct MappedRustDiagnostic { 161pub(crate) struct MappedRustDiagnostic {
159 pub location: Location, 162 pub location: Location,
160 pub diagnostic: Diagnostic, 163 pub diagnostic: Diagnostic,
161 pub fixes: Vec<CodeAction>, 164 pub fixes: Vec<lsp_ext::CodeAction>,
162} 165}
163 166
164/// Converts a Rust root diagnostic to LSP form 167/// Converts a Rust root diagnostic to LSP form
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 313a8c769..c25d90a50 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -1,6 +1,6 @@
1//! rust-analyzer extensions to the LSP. 1//! rust-analyzer extensions to the LSP.
2 2
3use std::path::PathBuf; 3use std::{collections::HashMap, path::PathBuf};
4 4
5use lsp_types::request::Request; 5use lsp_types::request::Request;
6use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; 6use lsp_types::{Location, Position, Range, TextDocumentIdentifier};
@@ -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: lsp_types::WorkspaceEdit,
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)]
@@ -183,3 +175,56 @@ pub struct SsrParams {
183 pub query: String, 175 pub query: String,
184 pub parse_only: bool, 176 pub parse_only: bool,
185} 177}
178
179pub enum CodeActionRequest {}
180
181impl Request for CodeActionRequest {
182 type Params = lsp_types::CodeActionParams;
183 type Result = Option<Vec<CodeAction>>;
184 const METHOD: &'static str = "textDocument/codeAction";
185}
186
187#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
188pub struct CodeAction {
189 pub title: String,
190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub group: Option<String>,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub kind: Option<String>,
194 #[serde(skip_serializing_if = "Option::is_none")]
195 pub command: Option<lsp_types::Command>,
196 #[serde(skip_serializing_if = "Option::is_none")]
197 pub edit: Option<SnippetWorkspaceEdit>,
198}
199
200#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
201#[serde(rename_all = "camelCase")]
202pub struct SnippetWorkspaceEdit {
203 #[serde(skip_serializing_if = "Option::is_none")]
204 pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>,
205 #[serde(skip_serializing_if = "Option::is_none")]
206 pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>,
207}
208
209#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
210#[serde(untagged, rename_all = "lowercase")]
211pub enum SnippetDocumentChangeOperation {
212 Op(lsp_types::ResourceOp),
213 Edit(SnippetTextDocumentEdit),
214}
215
216#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
217#[serde(rename_all = "camelCase")]
218pub struct SnippetTextDocumentEdit {
219 pub text_document: lsp_types::VersionedTextDocumentIdentifier,
220 pub edits: Vec<SnippetTextEdit>,
221}
222
223#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
224#[serde(rename_all = "camelCase")]
225pub struct SnippetTextEdit {
226 pub range: Range,
227 pub new_text: String,
228 #[serde(skip_serializing_if = "Option::is_none")]
229 pub insert_text_format: Option<lsp_types::InsertTextFormat>,
230}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 15e5bb354..87795fffb 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -518,6 +518,7 @@ fn on_request(
518 .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)? 518 .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)?
519 .on::<lsp_ext::Runnables>(handlers::handle_runnables)? 519 .on::<lsp_ext::Runnables>(handlers::handle_runnables)?
520 .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)? 520 .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)?
521 .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)?
521 .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)? 522 .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)?
522 .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)? 523 .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)?
523 .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)? 524 .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
@@ -525,7 +526,6 @@ fn on_request(
525 .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)? 526 .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)?
526 .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)? 527 .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)?
527 .on::<lsp_types::request::Completion>(handlers::handle_completion)? 528 .on::<lsp_types::request::Completion>(handlers::handle_completion)?
528 .on::<lsp_types::request::CodeActionRequest>(handlers::handle_code_action)?
529 .on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)? 529 .on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)?
530 .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)? 530 .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)?
531 .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)? 531 .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)?
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index e67556752..ba6857556 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -11,15 +11,15 @@ use lsp_server::ErrorCode;
11use lsp_types::{ 11use lsp_types::{
12 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, 12 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
13 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, 13 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
14 CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, 14 CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight,
15 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, 15 DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location,
16 Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, 16 MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams,
17 Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams, 17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
18 SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, 18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit,
19 TextEdit, Url, WorkspaceEdit,
20}; 19};
21use ra_ide::{ 20use ra_ide::{
22 Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, 21 FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
22 TextEdit,
23}; 23};
24use ra_prof::profile; 24use ra_prof::profile;
25use ra_project_model::TargetKind; 25use ra_project_model::TargetKind;
@@ -150,22 +150,35 @@ pub fn handle_find_matching_brace(
150pub fn handle_join_lines( 150pub fn handle_join_lines(
151 world: WorldSnapshot, 151 world: WorldSnapshot,
152 params: lsp_ext::JoinLinesParams, 152 params: lsp_ext::JoinLinesParams,
153) -> Result<lsp_ext::SourceChange> { 153) -> Result<Vec<lsp_types::TextEdit>> {
154 let _p = profile("handle_join_lines"); 154 let _p = profile("handle_join_lines");
155 let frange = from_proto::file_range(&world, params.text_document, params.range)?; 155 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
156 let source_change = world.analysis().join_lines(frange)?; 156 let line_index = world.analysis().file_line_index(file_id)?;
157 to_proto::source_change(&world, source_change) 157 let line_endings = world.file_line_endings(file_id);
158 let mut res = TextEdit::default();
159 for range in params.ranges {
160 let range = from_proto::text_range(&line_index, range);
161 let edit = world.analysis().join_lines(FileRange { file_id, range })?;
162 match res.union(edit) {
163 Ok(()) => (),
164 Err(_edit) => {
165 // just ignore overlapping edits
166 }
167 }
168 }
169 let res = to_proto::text_edit_vec(&line_index, line_endings, res);
170 Ok(res)
158} 171}
159 172
160pub fn handle_on_enter( 173pub fn handle_on_enter(
161 world: WorldSnapshot, 174 world: WorldSnapshot,
162 params: lsp_types::TextDocumentPositionParams, 175 params: lsp_types::TextDocumentPositionParams,
163) -> Result<Option<lsp_ext::SourceChange>> { 176) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> {
164 let _p = profile("handle_on_enter"); 177 let _p = profile("handle_on_enter");
165 let position = from_proto::file_position(&world, params)?; 178 let position = from_proto::file_position(&world, params)?;
166 match world.analysis().on_enter(position)? { 179 match world.analysis().on_enter(position)? {
167 None => Ok(None), 180 None => Ok(None),
168 Some(source_change) => to_proto::source_change(&world, source_change).map(Some), 181 Some(source_change) => to_proto::snippet_workspace_edit(&world, source_change).map(Some),
169 } 182 }
170} 183}
171 184
@@ -173,7 +186,7 @@ pub fn handle_on_enter(
173pub fn handle_on_type_formatting( 186pub fn handle_on_type_formatting(
174 world: WorldSnapshot, 187 world: WorldSnapshot,
175 params: lsp_types::DocumentOnTypeFormattingParams, 188 params: lsp_types::DocumentOnTypeFormattingParams,
176) -> Result<Option<Vec<TextEdit>>> { 189) -> Result<Option<Vec<lsp_types::TextEdit>>> {
177 let _p = profile("handle_on_type_formatting"); 190 let _p = profile("handle_on_type_formatting");
178 let mut position = from_proto::file_position(&world, params.text_document_position)?; 191 let mut position = from_proto::file_position(&world, params.text_document_position)?;
179 let line_index = world.analysis().file_line_index(position.file_id)?; 192 let line_index = world.analysis().file_line_index(position.file_id)?;
@@ -268,7 +281,7 @@ pub fn handle_document_symbol(
268 kind: symbol.kind, 281 kind: symbol.kind,
269 deprecated: symbol.deprecated, 282 deprecated: symbol.deprecated,
270 location: Location::new(url.clone(), symbol.range), 283 location: Location::new(url.clone(), symbol.range),
271 container_name: container_name, 284 container_name,
272 }); 285 });
273 286
274 for child in symbol.children.iter().flatten() { 287 for child in symbol.children.iter().flatten() {
@@ -476,7 +489,7 @@ pub fn handle_completion(
476 return Ok(None); 489 return Ok(None);
477 } 490 }
478 491
479 let items = match world.analysis().completions(position, &world.config.completion)? { 492 let items = match world.analysis().completions(&world.config.completion, position)? {
480 None => return Ok(None), 493 None => return Ok(None),
481 Some(items) => items, 494 Some(items) => items,
482 }; 495 };
@@ -585,9 +598,8 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio
585 None => return Ok(None), 598 None => return Ok(None),
586 Some(it) => it.info, 599 Some(it) => it.info,
587 }; 600 };
588 601 let workspace_edit = to_proto::workspace_edit(&world, source_change)?;
589 let source_change = to_proto::source_change(&world, source_change)?; 602 Ok(Some(workspace_edit))
590 Ok(Some(source_change.workspace_edit))
591} 603}
592 604
593pub fn handle_references( 605pub fn handle_references(
@@ -620,7 +632,7 @@ pub fn handle_references(
620pub fn handle_formatting( 632pub fn handle_formatting(
621 world: WorldSnapshot, 633 world: WorldSnapshot,
622 params: DocumentFormattingParams, 634 params: DocumentFormattingParams,
623) -> Result<Option<Vec<TextEdit>>> { 635) -> Result<Option<Vec<lsp_types::TextEdit>>> {
624 let _p = profile("handle_formatting"); 636 let _p = profile("handle_formatting");
625 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 637 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
626 let file = world.analysis().file_text(file_id)?; 638 let file = world.analysis().file_text(file_id)?;
@@ -687,7 +699,7 @@ pub fn handle_formatting(
687 } 699 }
688 } 700 }
689 701
690 Ok(Some(vec![TextEdit { 702 Ok(Some(vec![lsp_types::TextEdit {
691 range: Range::new(Position::new(0, 0), end_position), 703 range: Range::new(Position::new(0, 0), end_position),
692 new_text: captured_stdout, 704 new_text: captured_stdout,
693 }])) 705 }]))
@@ -696,14 +708,22 @@ pub fn handle_formatting(
696pub fn handle_code_action( 708pub fn handle_code_action(
697 world: WorldSnapshot, 709 world: WorldSnapshot,
698 params: lsp_types::CodeActionParams, 710 params: lsp_types::CodeActionParams,
699) -> Result<Option<CodeActionResponse>> { 711) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
700 let _p = profile("handle_code_action"); 712 let _p = profile("handle_code_action");
713 // We intentionally don't support command-based actions, as those either
714 // requires custom client-code anyway, or requires server-initiated edits.
715 // Server initiated edits break causality, so we avoid those as well.
716 if !world.config.client_caps.code_action_literals {
717 return Ok(None);
718 }
719
701 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 720 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
702 let line_index = world.analysis().file_line_index(file_id)?; 721 let line_index = world.analysis().file_line_index(file_id)?;
703 let range = from_proto::text_range(&line_index, params.range); 722 let range = from_proto::text_range(&line_index, params.range);
723 let frange = FileRange { file_id, range };
704 724
705 let diagnostics = world.analysis().diagnostics(file_id)?; 725 let diagnostics = world.analysis().diagnostics(file_id)?;
706 let mut res = CodeActionResponse::default(); 726 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
707 727
708 let fixes_from_diagnostics = diagnostics 728 let fixes_from_diagnostics = diagnostics
709 .into_iter() 729 .into_iter()
@@ -711,24 +731,12 @@ pub fn handle_code_action(
711 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) 731 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some())
712 .map(|(_range, fix)| fix); 732 .map(|(_range, fix)| fix);
713 733
714 for source_edit in fixes_from_diagnostics { 734 for fix in fixes_from_diagnostics {
715 let title = source_edit.label.clone(); 735 let title = fix.label;
716 let edit = to_proto::source_change(&world, source_edit)?; 736 let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?;
717 737 let action =
718 let command = Command { 738 lsp_ext::CodeAction { title, group: None, kind: None, edit: Some(edit), command: None };
719 title, 739 res.push(action);
720 command: "rust-analyzer.applySourceChange".to_string(),
721 arguments: Some(vec![to_value(edit).unwrap()]),
722 };
723 let action = CodeAction {
724 title: command.title.clone(),
725 kind: None,
726 diagnostics: None,
727 edit: None,
728 command: Some(command),
729 is_preferred: None,
730 };
731 res.push(action.into());
732 } 740 }
733 741
734 for fix in world.check_fixes.get(&file_id).into_iter().flatten() { 742 for fix in world.check_fixes.get(&file_id).into_iter().flatten() {
@@ -739,70 +747,8 @@ pub fn handle_code_action(
739 res.push(fix.action.clone()); 747 res.push(fix.action.clone());
740 } 748 }
741 749
742 let mut grouped_assists: FxHashMap<String, (usize, Vec<Assist>)> = FxHashMap::default(); 750 for assist in world.analysis().assists(&world.config.assist, frange)?.into_iter() {
743 for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() { 751 res.push(to_proto::code_action(&world, assist)?.into());
744 match &assist.group_label {
745 Some(label) => grouped_assists
746 .entry(label.to_owned())
747 .or_insert_with(|| {
748 let idx = res.len();
749 let dummy = Command::new(String::new(), String::new(), None);
750 res.push(dummy.into());
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] = CodeAction {
779 title,
780 kind: None,
781 diagnostics: None,
782 edit: None,
783 command,
784 is_preferred: None,
785 }
786 .into();
787 }
788 }
789
790 // If the client only supports commands then filter the list
791 // and remove and actions that depend on edits.
792 if !world.config.client_caps.code_action_literals {
793 // FIXME: use drain_filter once it hits stable.
794 res = res
795 .into_iter()
796 .filter_map(|it| match it {
797 cmd @ lsp_types::CodeActionOrCommand::Command(_) => Some(cmd),
798 lsp_types::CodeActionOrCommand::CodeAction(action) => match action.command {
799 Some(cmd) if action.edit.is_none() => {
800 Some(lsp_types::CodeActionOrCommand::Command(cmd))
801 }
802 _ => None,
803 },
804 })
805 .collect();
806 } 752 }
807 Ok(Some(res)) 753 Ok(Some(res))
808} 754}
@@ -828,11 +774,11 @@ pub fn handle_code_lens(
828 for runnable in world.analysis().runnables(file_id)? { 774 for runnable in world.analysis().runnables(file_id)? {
829 let (run_title, debugee) = match &runnable.kind { 775 let (run_title, debugee) = match &runnable.kind {
830 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { 776 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => {
831 ("▶\u{fe0e}Run Test", true) 777 ("▶\u{fe0e} Run Test", true)
832 } 778 }
833 RunnableKind::DocTest { .. } => { 779 RunnableKind::DocTest { .. } => {
834 // cargo does not support -no-run for doctests 780 // cargo does not support -no-run for doctests
835 ("▶\u{fe0e}Run Doctest", false) 781 ("▶\u{fe0e} Run Doctest", false)
836 } 782 }
837 RunnableKind::Bench { .. } => { 783 RunnableKind::Bench { .. } => {
838 // Nothing wrong with bench debugging 784 // Nothing wrong with bench debugging
@@ -998,11 +944,11 @@ pub fn handle_document_highlight(
998pub fn handle_ssr( 944pub fn handle_ssr(
999 world: WorldSnapshot, 945 world: WorldSnapshot,
1000 params: lsp_ext::SsrParams, 946 params: lsp_ext::SsrParams,
1001) -> Result<lsp_ext::SourceChange> { 947) -> Result<lsp_types::WorkspaceEdit> {
1002 let _p = profile("handle_ssr"); 948 let _p = profile("handle_ssr");
1003 let source_change = 949 let source_change =
1004 world.analysis().structural_search_replace(&params.query, params.parse_only)??; 950 world.analysis().structural_search_replace(&params.query, params.parse_only)??;
1005 to_proto::source_change(&world, source_change) 951 to_proto::workspace_edit(&world, source_change)
1006} 952}
1007 953
1008pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { 954pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> {
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 09343f925..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};
@@ -112,16 +111,28 @@ pub(crate) fn text_edit(
112 lsp_types::TextEdit { range, new_text } 111 lsp_types::TextEdit { range, new_text }
113} 112}
114 113
114pub(crate) fn snippet_text_edit(
115 line_index: &LineIndex,
116 line_endings: LineEndings,
117 is_snippet: bool,
118 indel: Indel,
119) -> lsp_ext::SnippetTextEdit {
120 let text_edit = text_edit(line_index, line_endings, indel);
121 let insert_text_format =
122 if is_snippet { Some(lsp_types::InsertTextFormat::Snippet) } else { None };
123 lsp_ext::SnippetTextEdit {
124 range: text_edit.range,
125 new_text: text_edit.new_text,
126 insert_text_format,
127 }
128}
129
115pub(crate) fn text_edit_vec( 130pub(crate) fn text_edit_vec(
116 line_index: &LineIndex, 131 line_index: &LineIndex,
117 line_endings: LineEndings, 132 line_endings: LineEndings,
118 text_edit: TextEdit, 133 text_edit: TextEdit,
119) -> Vec<lsp_types::TextEdit> { 134) -> Vec<lsp_types::TextEdit> {
120 text_edit 135 text_edit.into_iter().map(|indel| self::text_edit(line_index, line_endings, indel)).collect()
121 .as_indels()
122 .iter()
123 .map(|it| self::text_edit(line_index, line_endings, it.clone()))
124 .collect()
125} 136}
126 137
127pub(crate) fn completion_item( 138pub(crate) fn completion_item(
@@ -134,7 +145,7 @@ pub(crate) fn completion_item(
134 // 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
135 // non-trivial mapping here. 146 // non-trivial mapping here.
136 let source_range = completion_item.source_range(); 147 let source_range = completion_item.source_range();
137 for indel in completion_item.text_edit().as_indels() { 148 for indel in completion_item.text_edit().iter() {
138 if indel.delete.contains_range(source_range) { 149 if indel.delete.contains_range(source_range) {
139 text_edit = Some(if indel.delete == source_range { 150 text_edit = Some(if indel.delete == source_range {
140 self::text_edit(line_index, line_endings, indel.clone()) 151 self::text_edit(line_index, line_endings, indel.clone())
@@ -360,14 +371,6 @@ pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result<lsp_types::U
360 world.file_id_to_uri(file_id) 371 world.file_id_to_uri(file_id)
361} 372}
362 373
363pub(crate) fn text_document_identifier(
364 world: &WorldSnapshot,
365 file_id: FileId,
366) -> Result<lsp_types::TextDocumentIdentifier> {
367 let res = lsp_types::TextDocumentIdentifier { uri: url(world, file_id)? };
368 Ok(res)
369}
370
371pub(crate) fn versioned_text_document_identifier( 374pub(crate) fn versioned_text_document_identifier(
372 world: &WorldSnapshot, 375 world: &WorldSnapshot,
373 file_id: FileId, 376 file_id: FileId,
@@ -442,20 +445,20 @@ pub(crate) fn goto_definition_response(
442 } 445 }
443} 446}
444 447
445pub(crate) fn text_document_edit( 448pub(crate) fn snippet_text_document_edit(
446 world: &WorldSnapshot, 449 world: &WorldSnapshot,
450 is_snippet: bool,
447 source_file_edit: SourceFileEdit, 451 source_file_edit: SourceFileEdit,
448) -> Result<lsp_types::TextDocumentEdit> { 452) -> Result<lsp_ext::SnippetTextDocumentEdit> {
449 let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; 453 let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?;
450 let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; 454 let line_index = world.analysis().file_line_index(source_file_edit.file_id)?;
451 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);
452 let edits = source_file_edit 456 let edits = source_file_edit
453 .edit 457 .edit
454 .as_indels() 458 .into_iter()
455 .iter() 459 .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it))
456 .map(|it| text_edit(&line_index, line_endings, it.clone()))
457 .collect(); 460 .collect();
458 Ok(lsp_types::TextDocumentEdit { text_document, edits }) 461 Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
459} 462}
460 463
461pub(crate) fn resource_op( 464pub(crate) fn resource_op(
@@ -476,45 +479,65 @@ pub(crate) fn resource_op(
476 Ok(res) 479 Ok(res)
477} 480}
478 481
479pub(crate) fn source_change( 482pub(crate) fn snippet_workspace_edit(
480 world: &WorldSnapshot, 483 world: &WorldSnapshot,
481 source_change: SourceChange, 484 source_change: SourceChange,
482) -> Result<lsp_ext::SourceChange> { 485) -> Result<lsp_ext::SnippetWorkspaceEdit> {
483 let cursor_position = match source_change.cursor_position { 486 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
484 None => None,
485 Some(pos) => {
486 let line_index = world.analysis().file_line_index(pos.file_id)?;
487 let edit = source_change
488 .source_file_edits
489 .iter()
490 .find(|it| it.file_id == pos.file_id)
491 .map(|it| &it.edit);
492 let line_col = match edit {
493 Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit),
494 None => line_index.line_col(pos.offset),
495 };
496 let position =
497 lsp_types::Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16));
498 Some(lsp_types::TextDocumentPositionParams {
499 text_document: text_document_identifier(world, pos.file_id)?,
500 position,
501 })
502 }
503 };
504 let mut document_changes: Vec<lsp_types::DocumentChangeOperation> = Vec::new();
505 for op in source_change.file_system_edits { 487 for op in source_change.file_system_edits {
506 let op = resource_op(&world, op)?; 488 let op = resource_op(&world, op)?;
507 document_changes.push(lsp_types::DocumentChangeOperation::Op(op)); 489 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op));
508 } 490 }
509 for edit in source_change.source_file_edits { 491 for edit in source_change.source_file_edits {
510 let edit = text_document_edit(&world, edit)?; 492 let edit = snippet_text_document_edit(&world, source_change.is_snippet, edit)?;
511 document_changes.push(lsp_types::DocumentChangeOperation::Edit(edit)); 493 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
494 }
495 let workspace_edit =
496 lsp_ext::SnippetWorkspaceEdit { changes: None, document_changes: Some(document_changes) };
497 Ok(workspace_edit)
498}
499
500pub(crate) fn workspace_edit(
501 world: &WorldSnapshot,
502 source_change: SourceChange,
503) -> Result<lsp_types::WorkspaceEdit> {
504 assert!(!source_change.is_snippet);
505 snippet_workspace_edit(world, source_change).map(|it| it.into())
506}
507
508impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
509 fn from(snippet_workspace_edit: lsp_ext::SnippetWorkspaceEdit) -> lsp_types::WorkspaceEdit {
510 lsp_types::WorkspaceEdit {
511 changes: None,
512 document_changes: snippet_workspace_edit.document_changes.map(|changes| {
513 lsp_types::DocumentChanges::Operations(
514 changes
515 .into_iter()
516 .map(|change| match change {
517 lsp_ext::SnippetDocumentChangeOperation::Op(op) => {
518 lsp_types::DocumentChangeOperation::Op(op)
519 }
520 lsp_ext::SnippetDocumentChangeOperation::Edit(edit) => {
521 lsp_types::DocumentChangeOperation::Edit(
522 lsp_types::TextDocumentEdit {
523 text_document: edit.text_document,
524 edits: edit
525 .edits
526 .into_iter()
527 .map(|edit| lsp_types::TextEdit {
528 range: edit.range,
529 new_text: edit.new_text,
530 })
531 .collect(),
532 },
533 )
534 }
535 })
536 .collect(),
537 )
538 }),
539 }
512 } 540 }
513 let workspace_edit = lsp_types::WorkspaceEdit {
514 changes: None,
515 document_changes: Some(lsp_types::DocumentChanges::Operations(document_changes)),
516 };
517 Ok(lsp_ext::SourceChange { label: source_change.label, workspace_edit, cursor_position })
518} 541}
519 542
520pub fn call_hierarchy_item( 543pub fn call_hierarchy_item(
@@ -572,22 +595,13 @@ fn main() <fold>{
572 } 595 }
573} 596}
574 597
575pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_types::CodeAction> { 598pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> {
576 let source_change = source_change(&world, assist.source_change)?; 599 let res = lsp_ext::CodeAction {
577 let arg = serde_json::to_value(source_change)?; 600 title: assist.label,
578 let title = assist.label; 601 group: if world.config.client_caps.code_action_group { assist.group_label } else { None },
579 let command = lsp_types::Command {
580 title: title.clone(),
581 command: "rust-analyzer.applySourceChange".to_string(),
582 arguments: Some(vec![arg]),
583 };
584
585 Ok(lsp_types::CodeAction {
586 title,
587 kind: Some(String::new()), 602 kind: Some(String::new()),
588 diagnostics: None, 603 edit: Some(snippet_workspace_edit(world, assist.source_change)?),
589 edit: None, 604 command: None,
590 command: Some(command), 605 };
591 is_preferred: None, 606 Ok(res)
592 })
593} 607}
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 5011cc273..738a9a8e3 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -333,29 +333,17 @@ fn main() {}
333 partial_result_params: PartialResultParams::default(), 333 partial_result_params: PartialResultParams::default(),
334 work_done_progress_params: WorkDoneProgressParams::default(), 334 work_done_progress_params: WorkDoneProgressParams::default(),
335 }, 335 },
336 json!([ 336 json!([{
337 { 337 "edit": {
338 "command": { 338 "documentChanges": [
339 "arguments": [
340 { 339 {
341 "cursorPosition": null, 340 "kind": "create",
342 "label": "Create module", 341 "uri": "file:///[..]/src/bar.rs"
343 "workspaceEdit": {
344 "documentChanges": [
345 {
346 "kind": "create",
347 "uri": "file:///[..]/src/bar.rs"
348 }
349 ]
350 }
351 } 342 }
352 ], 343 ]
353 "command": "rust-analyzer.applySourceChange",
354 "title": "Create module"
355 }, 344 },
356 "title": "Create module" 345 "title": "Create module"
357 } 346 }]),
358 ]),
359 ); 347 );
360 348
361 server.request::<CodeActionRequest>( 349 server.request::<CodeActionRequest>(
@@ -416,29 +404,17 @@ fn main() {{}}
416 partial_result_params: PartialResultParams::default(), 404 partial_result_params: PartialResultParams::default(),
417 work_done_progress_params: WorkDoneProgressParams::default(), 405 work_done_progress_params: WorkDoneProgressParams::default(),
418 }, 406 },
419 json!([ 407 json!([{
420 { 408 "edit": {
421 "command": { 409 "documentChanges": [
422 "arguments": [
423 { 410 {
424 "cursorPosition": null, 411 "kind": "create",
425 "label": "Create module", 412 "uri": "file://[..]/src/bar.rs"
426 "workspaceEdit": {
427 "documentChanges": [
428 {
429 "kind": "create",
430 "uri": "file:///[..]/src/bar.rs"
431 }
432 ]
433 }
434 } 413 }
435 ], 414 ]
436 "command": "rust-analyzer.applySourceChange",
437 "title": "Create module"
438 }, 415 },
439 "title": "Create module" 416 "title": "Create module"
440 } 417 }]),
441 ]),
442 ); 418 );
443 419
444 server.request::<CodeActionRequest>( 420 server.request::<CodeActionRequest>(
@@ -498,27 +474,21 @@ fn main() {{}}
498 position: Position { line: 0, character: 5 }, 474 position: Position { line: 0, character: 5 },
499 }, 475 },
500 json!({ 476 json!({
501 "cursorPosition": { 477 "documentChanges": [
502 "position": { "character": 4, "line": 1 }, 478 {
503 "textDocument": { "uri": "file:///[..]src/m0.rs" } 479 "edits": [
504 }, 480 {
505 "label": "On enter", 481 "insertTextFormat": 2,
506 "workspaceEdit": { 482 "newText": "\n/// $0",
507 "documentChanges": [ 483 "range": {
508 { 484 "end": { "character": 5, "line": 0 },
509 "edits": [ 485 "start": { "character": 5, "line": 0 }
510 {
511 "newText": "\n/// ",
512 "range": {
513 "end": { "character": 5, "line": 0 },
514 "start": { "character": 5, "line": 0 }
515 }
516 } 486 }
517 ], 487 }
518 "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null } 488 ],
519 } 489 "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null }
520 ] 490 }
521 } 491 ]
522 }), 492 }),
523 ); 493 );
524 let elapsed = start.elapsed(); 494 let elapsed = start.elapsed();
@@ -550,27 +520,21 @@ version = \"0.0.0\"
550 position: Position { line: 0, character: 8 }, 520 position: Position { line: 0, character: 8 },
551 }, 521 },
552 json!({ 522 json!({
553 "cursorPosition": { 523 "documentChanges": [
554 "position": { "line": 1, "character": 4 }, 524 {
555 "textDocument": { "uri": "file:///[..]src/main.rs" } 525 "edits": [
556 }, 526 {
557 "label": "On enter", 527 "insertTextFormat": 2,
558 "workspaceEdit": { 528 "newText": "\r\n/// $0",
559 "documentChanges": [ 529 "range": {
560 { 530 "end": { "line": 0, "character": 8 },
561 "edits": [ 531 "start": { "line": 0, "character": 8 }
562 {
563 "newText": "\r\n/// ",
564 "range": {
565 "end": { "line": 0, "character": 8 },
566 "start": { "line": 0, "character": 8 }
567 }
568 } 532 }
569 ], 533 }
570 "textDocument": { "uri": "file:///[..]src/main.rs", "version": null } 534 ],
571 } 535 "textDocument": { "uri": "file:///[..]src/main.rs", "version": null }
572 ] 536 }
573 } 537 ]
574 }), 538 }),
575 ); 539 );
576} 540}
@@ -810,5 +774,5 @@ pub fn foo(_input: TokenStream) -> TokenStream {
810 }); 774 });
811 775
812 let value = res.get("contents").unwrap().get("value").unwrap().to_string(); 776 let value = res.get("contents").unwrap().get("value").unwrap().to_string();
813 assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#) 777 assert_eq!(value, r#""foo::Bar\n___\n\n```rust\nfn bar()\n```""#)
814} 778}
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 0f34ce70e..71a57fba2 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -116,3 +116,11 @@ pub fn to_lower_snake_case(s: &str) -> String {
116 } 116 }
117 buf 117 buf
118} 118}
119
120pub fn replace(buf: &mut String, from: char, to: &str) {
121 if !buf.contains(from) {
122 return;
123 }
124 // FIXME: do this in place.
125 *buf = buf.replace(from, to)
126}
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index b1e3c328f..be2cfbaa2 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -7,7 +7,7 @@
7//! * marks (see the eponymous module). 7//! * marks (see the eponymous module).
8 8
9#[macro_use] 9#[macro_use]
10pub mod marks; 10pub mod mark;
11 11
12use std::{ 12use std::{
13 fs, 13 fs,
diff --git a/crates/test_utils/src/marks.rs b/crates/test_utils/src/mark.rs
index c3185e860..7c309a894 100644
--- a/crates/test_utils/src/marks.rs
+++ b/crates/test_utils/src/mark.rs
@@ -7,18 +7,18 @@
7//! ``` 7//! ```
8//! #[test] 8//! #[test]
9//! fn test_foo() { 9//! fn test_foo() {
10//! covers!(test_foo); 10//! mark::check!(test_foo);
11//! } 11//! }
12//! ``` 12//! ```
13//! 13//!
14//! and in the code under test you write 14//! and in the code under test you write
15//! 15//!
16//! ``` 16//! ```
17//! # use test_utils::tested_by; 17//! # use test_utils::mark;
18//! # fn some_condition() -> bool { true } 18//! # fn some_condition() -> bool { true }
19//! fn foo() { 19//! fn foo() {
20//! if some_condition() { 20//! if some_condition() {
21//! tested_by!(test_foo); 21//! mark::hit!(test_foo);
22//! } 22//! }
23//! } 23//! }
24//! ``` 24//! ```
@@ -29,43 +29,31 @@
29use std::sync::atomic::{AtomicUsize, Ordering}; 29use std::sync::atomic::{AtomicUsize, Ordering};
30 30
31#[macro_export] 31#[macro_export]
32macro_rules! tested_by { 32macro_rules! _hit {
33 ($ident:ident; force) => {{
34 {
35 // sic! use call-site crate
36 crate::marks::$ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
37 }
38 }};
39 ($ident:ident) => {{ 33 ($ident:ident) => {{
40 #[cfg(test)] 34 #[cfg(test)]
41 { 35 {
42 // sic! use call-site crate 36 extern "C" {
43 crate::marks::$ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst); 37 #[no_mangle]
38 static $ident: std::sync::atomic::AtomicUsize;
39 }
40 unsafe {
41 $ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
42 }
44 } 43 }
45 }}; 44 }};
46} 45}
46pub use _hit as hit;
47 47
48#[macro_export] 48#[macro_export]
49macro_rules! covers { 49macro_rules! _check {
50 // sic! use call-site crate
51 ($ident:ident) => { 50 ($ident:ident) => {
52 $crate::covers!(crate::$ident) 51 #[no_mangle]
53 }; 52 static $ident: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
54 ($krate:ident :: $ident:ident) => { 53 let _checker = $crate::mark::MarkChecker::new(&$ident);
55 let _checker = $crate::marks::MarkChecker::new(&$krate::marks::$ident);
56 };
57}
58
59#[macro_export]
60macro_rules! marks {
61 ($($ident:ident)*) => {
62 $(
63 #[allow(bad_style)]
64 pub static $ident: std::sync::atomic::AtomicUsize =
65 std::sync::atomic::AtomicUsize::new(0);
66 )*
67 }; 54 };
68} 55}
56pub use _check as check;
69 57
70pub struct MarkChecker { 58pub struct MarkChecker {
71 mark: &'static AtomicUsize, 59 mark: &'static AtomicUsize,