aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/assist_ctx.rs8
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs48
-rw-r--r--crates/ra_assists/src/handlers/add_from_impl_for_enum.rs2
-rw-r--r--crates/ra_assists/src/handlers/add_function.rs236
-rw-r--r--crates/ra_assists/src/handlers/introduce_variable.rs2
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs2
-rw-r--r--crates/ra_assists/src/handlers/split_import.rs7
-rw-r--r--crates/ra_assists/src/lib.rs25
-rw-r--r--crates/ra_db/src/fixture.rs2
-rw-r--r--crates/ra_db/src/input.rs2
-rw-r--r--crates/ra_hir/src/code_model.rs15
-rw-r--r--crates/ra_hir/src/semantics.rs4
-rw-r--r--crates/ra_hir/src/source_analyzer.rs11
-rw-r--r--crates/ra_hir_def/src/body/lower.rs8
-rw-r--r--crates/ra_hir_def/src/body/scope.rs11
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs32
-rw-r--r--crates/ra_hir_def/src/nameres/tests.rs9
-rw-r--r--crates/ra_hir_expand/Cargo.toml1
-rw-r--r--crates/ra_hir_expand/src/ast_id_map.rs2
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs8
-rw-r--r--crates/ra_hir_expand/src/proc_macro.rs106
-rw-r--r--crates/ra_hir_ty/Cargo.toml6
-rw-r--r--crates/ra_hir_ty/src/_match.rs336
-rw-r--r--crates/ra_hir_ty/src/db.rs7
-rw-r--r--crates/ra_hir_ty/src/infer.rs4
-rw-r--r--crates/ra_hir_ty/src/infer/pat.rs6
-rw-r--r--crates/ra_hir_ty/src/lib.rs2
-rw-r--r--crates/ra_hir_ty/src/test_db.rs36
-rw-r--r--crates/ra_hir_ty/src/tests.rs52
-rw-r--r--crates/ra_hir_ty/src/tests/patterns.rs26
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs41
-rw-r--r--crates/ra_hir_ty/src/traits.rs2
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs145
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/tls.rs33
-rw-r--r--crates/ra_ide/src/assists.rs4
-rw-r--r--crates/ra_ide/src/call_info.rs30
-rw-r--r--crates/ra_ide/src/completion.rs25
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs587
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs6
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs12
-rw-r--r--crates/ra_ide/src/completion/complete_macro_in_item_position.rs6
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs39
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs46
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs26
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs18
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs229
-rw-r--r--crates/ra_ide/src/completion/completion_config.rs35
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs31
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs38
-rw-r--r--crates/ra_ide/src/completion/presentation.rs419
-rw-r--r--crates/ra_ide/src/display/function_signature.rs69
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs2
-rw-r--r--crates/ra_ide/src/extend_selection.rs2
-rw-r--r--crates/ra_ide/src/goto_definition.rs28
-rw-r--r--crates/ra_ide/src/lib.rs4
-rw-r--r--crates/ra_ide/src/marks.rs2
-rw-r--r--crates/ra_ide/src/runnables.rs35
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html6
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html6
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs37
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs2
-rw-r--r--crates/ra_ide_db/src/defs.rs9
-rw-r--r--crates/ra_ide_db/src/marks.rs1
-rw-r--r--crates/ra_ide_db/src/symbol_index.rs2
-rw-r--r--crates/ra_mbe/src/mbe_expander/matcher.rs40
-rw-r--r--crates/ra_mbe/src/subtree_source.rs47
-rw-r--r--crates/ra_mbe/src/syntax_bridge.rs39
-rw-r--r--crates/ra_mbe/src/tests.rs57
-rw-r--r--crates/ra_mbe/src/tt_iter.rs8
-rw-r--r--crates/ra_proc_macro/src/lib.rs17
-rw-r--r--crates/ra_proc_macro/src/msg.rs37
-rw-r--r--crates/ra_proc_macro/src/process.rs118
-rw-r--r--crates/ra_proc_macro/src/rpc.rs18
-rw-r--r--crates/ra_proc_macro_srv/src/cli.rs69
-rw-r--r--crates/ra_proc_macro_srv/src/dylib.rs103
-rw-r--r--crates/ra_proc_macro_srv/src/lib.rs36
-rw-r--r--crates/ra_proc_macro_srv/src/rustc_server.rs13
-rw-r--r--crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt3
-rw-r--r--crates/ra_proc_macro_srv/src/tests/utils.rs2
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs8
-rw-r--r--crates/ra_syntax/src/algo.rs8
-rw-r--r--crates/ra_syntax/src/ast/edit.rs6
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs2924
-rw-r--r--crates/ra_syntax/src/ast/make.rs11
-rw-r--r--crates/ra_syntax/src/ptr.rs4
-rw-r--r--crates/rust-analyzer/src/bin/args.rs40
-rw-r--r--crates/rust-analyzer/src/bin/main.rs6
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs5
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs6
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs19
-rw-r--r--crates/rust-analyzer/src/conv.rs18
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs2
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs3
-rw-r--r--crates/rust-analyzer/src/world.rs25
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs11
98 files changed, 4565 insertions, 2139 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index c3e653299..279163257 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -10,7 +10,7 @@ use ra_syntax::{
10}; 10};
11use ra_text_edit::TextEditBuilder; 11use ra_text_edit::TextEditBuilder;
12 12
13use crate::{AssistAction, AssistId, AssistLabel, GroupLabel, ResolvedAssist}; 13use crate::{AssistAction, AssistFile, AssistId, AssistLabel, GroupLabel, ResolvedAssist};
14use algo::SyntaxRewriter; 14use algo::SyntaxRewriter;
15 15
16#[derive(Clone, Debug)] 16#[derive(Clone, Debug)]
@@ -180,6 +180,7 @@ pub(crate) struct ActionBuilder {
180 edit: TextEditBuilder, 180 edit: TextEditBuilder,
181 cursor_position: Option<TextUnit>, 181 cursor_position: Option<TextUnit>,
182 target: Option<TextRange>, 182 target: Option<TextRange>,
183 file: AssistFile,
183} 184}
184 185
185impl ActionBuilder { 186impl ActionBuilder {
@@ -241,11 +242,16 @@ impl ActionBuilder {
241 algo::diff(&node, &new).into_text_edit(&mut self.edit) 242 algo::diff(&node, &new).into_text_edit(&mut self.edit)
242 } 243 }
243 244
245 pub(crate) fn set_file(&mut self, assist_file: AssistFile) {
246 self.file = assist_file
247 }
248
244 fn build(self) -> AssistAction { 249 fn build(self) -> AssistAction {
245 AssistAction { 250 AssistAction {
246 edit: self.edit.finish(), 251 edit: self.edit.finish(),
247 cursor_position: self.cursor_position, 252 cursor_position: self.cursor_position,
248 target: self.target, 253 target: self.target,
254 file: self.file,
249 } 255 }
250 } 256 }
251} 257}
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs
index b39e60870..e4fa9ee36 100644
--- a/crates/ra_assists/src/doc_tests/generated.rs
+++ b/crates/ra_assists/src/doc_tests/generated.rs
@@ -66,7 +66,7 @@ fn doctest_add_function() {
66struct Baz; 66struct Baz;
67fn baz() -> Baz { Baz } 67fn baz() -> Baz { Baz }
68fn foo() { 68fn foo() {
69 bar<|>("", baz()); 69 bar<|>("", baz());
70} 70}
71 71
72"#####, 72"#####,
@@ -74,7 +74,7 @@ fn foo() {
74struct Baz; 74struct Baz;
75fn baz() -> Baz { Baz } 75fn baz() -> Baz { Baz }
76fn foo() { 76fn foo() {
77 bar("", baz()); 77 bar("", baz());
78} 78}
79 79
80fn bar(arg: &str, baz: Baz) { 80fn bar(arg: &str, baz: Baz) {
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index d86d804b2..6c56d93d8 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -52,21 +52,22 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
52 } 52 }
53 // Infer type 53 // Infer type
54 let ty = ctx.sema.type_of_expr(&expr)?; 54 let ty = ctx.sema.type_of_expr(&expr)?;
55 // Assist not applicable if the type is unknown 55
56 if ty.contains_unknown() { 56 if ty.contains_unknown() || ty.is_closure() {
57 return None; 57 return None;
58 } 58 }
59 59
60 let db = ctx.db; 60 let db = ctx.db;
61 let new_type_string = ty.display_truncated(db, None).to_string();
61 ctx.add_assist( 62 ctx.add_assist(
62 AssistId("add_explicit_type"), 63 AssistId("add_explicit_type"),
63 format!("Insert explicit type '{}'", ty.display(db)), 64 format!("Insert explicit type '{}'", new_type_string),
64 |edit| { 65 |edit| {
65 edit.target(pat_range); 66 edit.target(pat_range);
66 if let Some(ascribed_ty) = ascribed_ty { 67 if let Some(ascribed_ty) = ascribed_ty {
67 edit.replace(ascribed_ty.syntax().text_range(), format!("{}", ty.display(db))); 68 edit.replace(ascribed_ty.syntax().text_range(), new_type_string);
68 } else { 69 } else {
69 edit.insert(name_range.end(), format!(": {}", ty.display(db))); 70 edit.insert(name_range.end(), format!(": {}", new_type_string));
70 } 71 }
71 }, 72 },
72 ) 73 )
@@ -174,4 +175,41 @@ mod tests {
174 "fn f() <|>{let a = match 1 {2 => 3, 3 => 5};}", 175 "fn f() <|>{let a = match 1 {2 => 3, 3 => 5};}",
175 ) 176 )
176 } 177 }
178
179 #[test]
180 fn closure_parameters_are_not_added() {
181 check_assist_not_applicable(
182 add_explicit_type,
183 r#"
184fn main() {
185 let multiply_by_two<|> = |i| i * 3;
186 let six = multiply_by_two(2);
187}"#,
188 )
189 }
190
191 #[test]
192 fn default_generics_should_not_be_added() {
193 check_assist(
194 add_explicit_type,
195 r#"
196struct Test<K, T = u8> {
197 k: K,
198 t: T,
199}
200
201fn main() {
202 let test<|> = Test { t: 23, k: 33 };
203}"#,
204 r#"
205struct Test<K, T = u8> {
206 k: K,
207 t: T,
208}
209
210fn main() {
211 let test<|>: Test<i32> = Test { t: 23, k: 33 };
212}"#,
213 );
214 }
177} 215}
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 864373aa5..0621487e8 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
@@ -98,7 +98,7 @@ fn already_has_from_impl(
98 }; 98 };
99 let var_ty = hir_enum_var.fields(sema.db)[0].signature_ty(sema.db); 99 let var_ty = hir_enum_var.fields(sema.db)[0].signature_ty(sema.db);
100 100
101 e_ty.impls_trait(sema.db, from_trait, &[var_ty.clone()]) 101 e_ty.impls_trait(sema.db, from_trait, &[var_ty])
102} 102}
103 103
104#[cfg(test)] 104#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs
index ad4ab66ed..f185cffdb 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/add_function.rs
@@ -3,8 +3,8 @@ use ra_syntax::{
3 SyntaxKind, SyntaxNode, TextUnit, 3 SyntaxKind, SyntaxNode, TextUnit,
4}; 4};
5 5
6use crate::{Assist, AssistCtx, AssistId}; 6use crate::{Assist, AssistCtx, AssistFile, AssistId};
7use ast::{edit::IndentLevel, ArgListOwner, CallExpr, Expr}; 7use ast::{edit::IndentLevel, ArgListOwner, ModuleItemOwner};
8use hir::HirDisplay; 8use hir::HirDisplay;
9use rustc_hash::{FxHashMap, FxHashSet}; 9use rustc_hash::{FxHashMap, FxHashSet};
10 10
@@ -16,7 +16,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
16// struct Baz; 16// struct Baz;
17// fn baz() -> Baz { Baz } 17// fn baz() -> Baz { Baz }
18// fn foo() { 18// fn foo() {
19// bar<|>("", baz()); 19// bar<|>("", baz());
20// } 20// }
21// 21//
22// ``` 22// ```
@@ -25,7 +25,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
25// struct Baz; 25// struct Baz;
26// fn baz() -> Baz { Baz } 26// fn baz() -> Baz { Baz }
27// fn foo() { 27// fn foo() {
28// bar("", baz()); 28// bar("", baz());
29// } 29// }
30// 30//
31// fn bar(arg: &str, baz: Baz) { 31// fn bar(arg: &str, baz: Baz) {
@@ -38,21 +38,30 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> {
38 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; 38 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
39 let path = path_expr.path()?; 39 let path = path_expr.path()?;
40 40
41 if path.qualifier().is_some() {
42 return None;
43 }
44
45 if ctx.sema.resolve_path(&path).is_some() { 41 if ctx.sema.resolve_path(&path).is_some() {
46 // The function call already resolves, no need to add a function 42 // The function call already resolves, no need to add a function
47 return None; 43 return None;
48 } 44 }
49 45
50 let function_builder = FunctionBuilder::from_call(&ctx, &call)?; 46 let target_module = if let Some(qualifier) = path.qualifier() {
47 if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) =
48 ctx.sema.resolve_path(&qualifier)
49 {
50 Some(module.definition_source(ctx.sema.db))
51 } else {
52 return None;
53 }
54 } else {
55 None
56 };
57
58 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
51 59
52 ctx.add_assist(AssistId("add_function"), "Add function", |edit| { 60 ctx.add_assist(AssistId("add_function"), "Add function", |edit| {
53 edit.target(call.syntax().text_range()); 61 edit.target(call.syntax().text_range());
54 62
55 if let Some(function_template) = function_builder.render() { 63 if let Some(function_template) = function_builder.render() {
64 edit.set_file(function_template.file);
56 edit.set_cursor(function_template.cursor_offset); 65 edit.set_cursor(function_template.cursor_offset);
57 edit.insert(function_template.insert_offset, function_template.fn_def.to_string()); 66 edit.insert(function_template.insert_offset, function_template.fn_def.to_string());
58 } 67 }
@@ -63,29 +72,67 @@ struct FunctionTemplate {
63 insert_offset: TextUnit, 72 insert_offset: TextUnit,
64 cursor_offset: TextUnit, 73 cursor_offset: TextUnit,
65 fn_def: ast::SourceFile, 74 fn_def: ast::SourceFile,
75 file: AssistFile,
66} 76}
67 77
68struct FunctionBuilder { 78struct FunctionBuilder {
69 append_fn_at: SyntaxNode, 79 target: GeneratedFunctionTarget,
70 fn_name: ast::Name, 80 fn_name: ast::Name,
71 type_params: Option<ast::TypeParamList>, 81 type_params: Option<ast::TypeParamList>,
72 params: ast::ParamList, 82 params: ast::ParamList,
83 file: AssistFile,
84 needs_pub: bool,
73} 85}
74 86
75impl FunctionBuilder { 87impl FunctionBuilder {
76 fn from_call(ctx: &AssistCtx, call: &ast::CallExpr) -> Option<Self> { 88 /// Prepares a generated function that matches `call` in `generate_in`
77 let append_fn_at = next_space_for_fn(&call)?; 89 /// (or as close to `call` as possible, if `generate_in` is `None`)
78 let fn_name = fn_name(&call)?; 90 fn from_call(
91 ctx: &AssistCtx,
92 call: &ast::CallExpr,
93 path: &ast::Path,
94 target_module: Option<hir::InFile<hir::ModuleSource>>,
95 ) -> Option<Self> {
96 let needs_pub = target_module.is_some();
97 let mut file = AssistFile::default();
98 let target = if let Some(target_module) = target_module {
99 let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, target_module)?;
100 file = in_file;
101 target
102 } else {
103 next_space_for_fn_after_call_site(&call)?
104 };
105 let fn_name = fn_name(&path)?;
79 let (type_params, params) = fn_args(ctx, &call)?; 106 let (type_params, params) = fn_args(ctx, &call)?;
80 Some(Self { append_fn_at, fn_name, type_params, params }) 107 Some(Self { target, fn_name, type_params, params, file, needs_pub })
81 } 108 }
109
82 fn render(self) -> Option<FunctionTemplate> { 110 fn render(self) -> Option<FunctionTemplate> {
83 let placeholder_expr = ast::make::expr_todo(); 111 let placeholder_expr = ast::make::expr_todo();
84 let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); 112 let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr));
85 let fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); 113 let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body);
86 let fn_def = ast::make::add_newlines(2, fn_def); 114 if self.needs_pub {
87 let fn_def = IndentLevel::from_node(&self.append_fn_at).increase_indent(fn_def); 115 fn_def = ast::make::add_pub_crate_modifier(fn_def);
88 let insert_offset = self.append_fn_at.text_range().end(); 116 }
117
118 let (fn_def, insert_offset) = match self.target {
119 GeneratedFunctionTarget::BehindItem(it) => {
120 let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def);
121 let indented = IndentLevel::from_node(&it).increase_indent(with_leading_blank_line);
122 (indented, it.text_range().end())
123 }
124 GeneratedFunctionTarget::InEmptyItemList(it) => {
125 let indent_once = IndentLevel(1);
126 let indent = IndentLevel::from_node(it.syntax());
127
128 let fn_def = ast::make::add_leading_newlines(1, fn_def);
129 let fn_def = indent_once.increase_indent(fn_def);
130 let fn_def = ast::make::add_trailing_newlines(1, fn_def);
131 let fn_def = indent.increase_indent(fn_def);
132 (fn_def, it.syntax().text_range().start() + TextUnit::from_usize(1))
133 }
134 };
135
89 let cursor_offset_from_fn_start = fn_def 136 let cursor_offset_from_fn_start = fn_def
90 .syntax() 137 .syntax()
91 .descendants() 138 .descendants()
@@ -94,19 +141,24 @@ impl FunctionBuilder {
94 .text_range() 141 .text_range()
95 .start(); 142 .start();
96 let cursor_offset = insert_offset + cursor_offset_from_fn_start; 143 let cursor_offset = insert_offset + cursor_offset_from_fn_start;
97 Some(FunctionTemplate { insert_offset, cursor_offset, fn_def }) 144 Some(FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file })
98 } 145 }
99} 146}
100 147
101fn fn_name(call: &CallExpr) -> Option<ast::Name> { 148enum GeneratedFunctionTarget {
102 let name = call.expr()?.syntax().to_string(); 149 BehindItem(SyntaxNode),
150 InEmptyItemList(ast::ItemList),
151}
152
153fn fn_name(call: &ast::Path) -> Option<ast::Name> {
154 let name = call.segment()?.syntax().to_string();
103 Some(ast::make::name(&name)) 155 Some(ast::make::name(&name))
104} 156}
105 157
106/// Computes the type variables and arguments required for the generated function 158/// Computes the type variables and arguments required for the generated function
107fn fn_args( 159fn fn_args(
108 ctx: &AssistCtx, 160 ctx: &AssistCtx,
109 call: &CallExpr, 161 call: &ast::CallExpr,
110) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { 162) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> {
111 let mut arg_names = Vec::new(); 163 let mut arg_names = Vec::new();
112 let mut arg_types = Vec::new(); 164 let mut arg_types = Vec::new();
@@ -158,9 +210,9 @@ fn deduplicate_arg_names(arg_names: &mut Vec<String>) {
158 } 210 }
159} 211}
160 212
161fn fn_arg_name(fn_arg: &Expr) -> Option<String> { 213fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> {
162 match fn_arg { 214 match fn_arg {
163 Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?), 215 ast::Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?),
164 _ => Some( 216 _ => Some(
165 fn_arg 217 fn_arg
166 .syntax() 218 .syntax()
@@ -172,7 +224,7 @@ fn fn_arg_name(fn_arg: &Expr) -> Option<String> {
172 } 224 }
173} 225}
174 226
175fn fn_arg_type(ctx: &AssistCtx, fn_arg: &Expr) -> Option<String> { 227fn fn_arg_type(ctx: &AssistCtx, fn_arg: &ast::Expr) -> Option<String> {
176 let ty = ctx.sema.type_of_expr(fn_arg)?; 228 let ty = ctx.sema.type_of_expr(fn_arg)?;
177 if ty.is_unknown() { 229 if ty.is_unknown() {
178 return None; 230 return None;
@@ -184,7 +236,7 @@ fn fn_arg_type(ctx: &AssistCtx, fn_arg: &Expr) -> Option<String> {
184/// directly after the current block 236/// directly after the current block
185/// We want to write the generated function directly after 237/// We want to write the generated function directly after
186/// fns, impls or macro calls, but inside mods 238/// fns, impls or macro calls, but inside mods
187fn next_space_for_fn(expr: &CallExpr) -> Option<SyntaxNode> { 239fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFunctionTarget> {
188 let mut ancestors = expr.syntax().ancestors().peekable(); 240 let mut ancestors = expr.syntax().ancestors().peekable();
189 let mut last_ancestor: Option<SyntaxNode> = None; 241 let mut last_ancestor: Option<SyntaxNode> = None;
190 while let Some(next_ancestor) = ancestors.next() { 242 while let Some(next_ancestor) = ancestors.next() {
@@ -201,7 +253,32 @@ fn next_space_for_fn(expr: &CallExpr) -> Option<SyntaxNode> {
201 } 253 }
202 last_ancestor = Some(next_ancestor); 254 last_ancestor = Some(next_ancestor);
203 } 255 }
204 last_ancestor 256 last_ancestor.map(GeneratedFunctionTarget::BehindItem)
257}
258
259fn next_space_for_fn_in_module(
260 db: &dyn hir::db::AstDatabase,
261 module: hir::InFile<hir::ModuleSource>,
262) -> Option<(AssistFile, GeneratedFunctionTarget)> {
263 let file = module.file_id.original_file(db);
264 let assist_file = AssistFile::TargetFile(file);
265 let assist_item = match module.value {
266 hir::ModuleSource::SourceFile(it) => {
267 if let Some(last_item) = it.items().last() {
268 GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
269 } else {
270 GeneratedFunctionTarget::BehindItem(it.syntax().clone())
271 }
272 }
273 hir::ModuleSource::Module(it) => {
274 if let Some(last_item) = it.item_list().and_then(|it| it.items().last()) {
275 GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
276 } else {
277 GeneratedFunctionTarget::InEmptyItemList(it.item_list()?)
278 }
279 }
280 };
281 Some((assist_file, assist_item))
205} 282}
206 283
207#[cfg(test)] 284#[cfg(test)]
@@ -714,6 +791,111 @@ fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) {
714 } 791 }
715 792
716 #[test] 793 #[test]
794 fn add_function_in_module() {
795 check_assist(
796 add_function,
797 r"
798mod bar {}
799
800fn foo() {
801 bar::my_fn<|>()
802}
803",
804 r"
805mod bar {
806 pub(crate) fn my_fn() {
807 <|>todo!()
808 }
809}
810
811fn foo() {
812 bar::my_fn()
813}
814",
815 )
816 }
817
818 #[test]
819 fn add_function_in_module_containing_other_items() {
820 check_assist(
821 add_function,
822 r"
823mod bar {
824 fn something_else() {}
825}
826
827fn foo() {
828 bar::my_fn<|>()
829}
830",
831 r"
832mod bar {
833 fn something_else() {}
834
835 pub(crate) fn my_fn() {
836 <|>todo!()
837 }
838}
839
840fn foo() {
841 bar::my_fn()
842}
843",
844 )
845 }
846
847 #[test]
848 fn add_function_in_nested_module() {
849 check_assist(
850 add_function,
851 r"
852mod bar {
853 mod baz {}
854}
855
856fn foo() {
857 bar::baz::my_fn<|>()
858}
859",
860 r"
861mod bar {
862 mod baz {
863 pub(crate) fn my_fn() {
864 <|>todo!()
865 }
866 }
867}
868
869fn foo() {
870 bar::baz::my_fn()
871}
872",
873 )
874 }
875
876 #[test]
877 fn add_function_in_another_file() {
878 check_assist(
879 add_function,
880 r"
881//- /main.rs
882mod foo;
883
884fn main() {
885 foo::bar<|>()
886}
887//- /foo.rs
888",
889 r"
890
891
892pub(crate) fn bar() {
893 <|>todo!()
894}",
895 )
896 }
897
898 #[test]
717 fn add_function_not_applicable_if_function_already_exists() { 899 fn add_function_not_applicable_if_function_already_exists() {
718 check_assist_not_applicable( 900 check_assist_not_applicable(
719 add_function, 901 add_function,
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs
index 8d0f7e922..8c09e6bcd 100644
--- a/crates/ra_assists/src/handlers/introduce_variable.rs
+++ b/crates/ra_assists/src/handlers/introduce_variable.rs
@@ -124,7 +124,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
124 } 124 }
125 } 125 }
126 126
127 if ast::Stmt::cast(node.clone().into()).is_some() { 127 if ast::Stmt::cast(node.clone()).is_some() {
128 return Some((node, false)); 128 return Some((node, false));
129 } 129 }
130 130
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs
index ef0ce0586..4be1238f1 100644
--- a/crates/ra_assists/src/handlers/merge_imports.rs
+++ b/crates/ra_assists/src/handlers/merge_imports.rs
@@ -30,7 +30,7 @@ pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> {
30 .filter_map(|dir| neighbor(&use_item, dir)) 30 .filter_map(|dir| neighbor(&use_item, dir))
31 .filter_map(|it| Some((it.clone(), it.use_tree()?))) 31 .filter_map(|it| Some((it.clone(), it.use_tree()?)))
32 .find_map(|(use_item, use_tree)| { 32 .find_map(|(use_item, use_tree)| {
33 Some((try_merge_trees(&tree, &use_tree)?, use_item.clone())) 33 Some((try_merge_trees(&tree, &use_tree)?, use_item))
34 })?; 34 })?;
35 35
36 rewriter.replace_ast(&tree, &merged); 36 rewriter.replace_ast(&tree, &merged);
diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs
index d9244f22d..f25826796 100644
--- a/crates/ra_assists/src/handlers/split_import.rs
+++ b/crates/ra_assists/src/handlers/split_import.rs
@@ -37,7 +37,7 @@ pub(crate) fn split_import(ctx: AssistCtx) -> Option<Assist> {
37 37
38#[cfg(test)] 38#[cfg(test)]
39mod tests { 39mod tests {
40 use crate::helpers::{check_assist, check_assist_target}; 40 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
41 41
42 use super::*; 42 use super::*;
43 43
@@ -63,4 +63,9 @@ mod tests {
63 fn split_import_target() { 63 fn split_import_target() {
64 check_assist_target(split_import, "use crate::<|>db::{RootDatabase, FileSymbol}", "::"); 64 check_assist_target(split_import, "use crate::<|>db::{RootDatabase, FileSymbol}", "::");
65 } 65 }
66
67 #[test]
68 fn issue4044() {
69 check_assist_not_applicable(split_import, "use crate::<|>:::self;")
70 }
66} 71}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index a00136da1..ccc95735f 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -17,7 +17,7 @@ mod doc_tests;
17pub mod utils; 17pub mod utils;
18pub mod ast_transform; 18pub mod ast_transform;
19 19
20use ra_db::FileRange; 20use ra_db::{FileId, FileRange};
21use ra_ide_db::RootDatabase; 21use ra_ide_db::RootDatabase;
22use ra_syntax::{TextRange, TextUnit}; 22use ra_syntax::{TextRange, TextUnit};
23use ra_text_edit::TextEdit; 23use ra_text_edit::TextEdit;
@@ -54,6 +54,7 @@ pub struct AssistAction {
54 pub cursor_position: Option<TextUnit>, 54 pub cursor_position: Option<TextUnit>,
55 // FIXME: This belongs to `AssistLabel` 55 // FIXME: This belongs to `AssistLabel`
56 pub target: Option<TextRange>, 56 pub target: Option<TextRange>,
57 pub file: AssistFile,
57} 58}
58 59
59#[derive(Debug, Clone)] 60#[derive(Debug, Clone)]
@@ -63,6 +64,18 @@ pub struct ResolvedAssist {
63 pub action: AssistAction, 64 pub action: AssistAction,
64} 65}
65 66
67#[derive(Debug, Clone, Copy)]
68pub enum AssistFile {
69 CurrentFile,
70 TargetFile(FileId),
71}
72
73impl Default for AssistFile {
74 fn default() -> Self {
75 Self::CurrentFile
76 }
77}
78
66/// Return all the assists applicable at the given position. 79/// Return all the assists applicable at the given position.
67/// 80///
68/// Assists are returned in the "unresolved" state, that is only labels are 81/// Assists are returned in the "unresolved" state, that is only labels are
@@ -184,7 +197,7 @@ mod helpers {
184 use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; 197 use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
185 use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset}; 198 use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset};
186 199
187 use crate::{AssistCtx, AssistHandler}; 200 use crate::{AssistCtx, AssistFile, AssistHandler};
188 use hir::Semantics; 201 use hir::Semantics;
189 202
190 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { 203 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
@@ -246,7 +259,13 @@ mod helpers {
246 (Some(assist), ExpectedResult::After(after)) => { 259 (Some(assist), ExpectedResult::After(after)) => {
247 let action = assist.0[0].action.clone().unwrap(); 260 let action = assist.0[0].action.clone().unwrap();
248 261
249 let mut actual = action.edit.apply(&text_without_caret); 262 let assisted_file_text = if let AssistFile::TargetFile(file_id) = action.file {
263 db.file_text(file_id).as_ref().to_owned()
264 } else {
265 text_without_caret
266 };
267
268 let mut actual = action.edit.apply(&assisted_file_text);
250 match action.cursor_position { 269 match action.cursor_position {
251 None => { 270 None => {
252 if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { 271 if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index 7777ce81e..8248684ee 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -235,7 +235,7 @@ fn parse_meta(meta: &str) -> ParsedMeta {
235 "env" => { 235 "env" => {
236 for key in value.split(',') { 236 for key in value.split(',') {
237 if let Some((k, v)) = split1(key, '=') { 237 if let Some((k, v)) = split1(key, '=') {
238 env.set(k.into(), v.into()); 238 env.set(k, v.into());
239 } 239 }
240 } 240 }
241 } 241 }
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 5ddce98c6..ab14e2d5e 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -327,7 +327,7 @@ impl ExternSource {
327 self.extern_paths.iter().find_map(|(root_path, id)| { 327 self.extern_paths.iter().find_map(|(root_path, id)| {
328 if let Ok(rel_path) = path.strip_prefix(root_path) { 328 if let Ok(rel_path) = path.strip_prefix(root_path) {
329 let rel_path = RelativePathBuf::from_path(rel_path).ok()?; 329 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
330 Some((id.clone(), rel_path)) 330 Some((*id, rel_path))
331 } else { 331 } else {
332 None 332 None
333 } 333 }
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 3801fce23..43f932e20 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -759,6 +759,17 @@ impl MacroDef {
759 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { 759 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
760 self.source(db).value.name().map(|it| it.as_name()) 760 self.source(db).value.name().map(|it| it.as_name())
761 } 761 }
762
763 /// Indicate it is a proc-macro
764 pub fn is_proc_macro(&self) -> bool {
765 match self.id.kind {
766 hir_expand::MacroDefKind::Declarative => false,
767 hir_expand::MacroDefKind::BuiltIn(_) => false,
768 hir_expand::MacroDefKind::BuiltInDerive(_) => false,
769 hir_expand::MacroDefKind::BuiltInEager(_) => false,
770 hir_expand::MacroDefKind::CustomDerive(_) => true,
771 }
772 }
762} 773}
763 774
764/// Invariant: `inner.as_assoc_item(db).is_some()` 775/// Invariant: `inner.as_assoc_item(db).is_some()`
@@ -1121,6 +1132,10 @@ impl Type {
1121 Some(self.ty.value.as_callable()?.0) 1132 Some(self.ty.value.as_callable()?.0)
1122 } 1133 }
1123 1134
1135 pub fn is_closure(&self) -> bool {
1136 matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { .. }, .. }))
1137 }
1138
1124 pub fn contains_unknown(&self) -> bool { 1139 pub fn contains_unknown(&self) -> bool {
1125 return go(&self.ty.value); 1140 return go(&self.ty.value);
1126 1141
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 0b477f0e9..5d6edc45c 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -195,6 +195,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
195 self.analyze(field.syntax()).resolve_record_field(self.db, field) 195 self.analyze(field.syntax()).resolve_record_field(self.db, field)
196 } 196 }
197 197
198 pub fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option<StructField> {
199 self.analyze(field.syntax()).resolve_record_field_pat(self.db, field)
200 }
201
198 pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> { 202 pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> {
199 let sa = self.analyze(macro_call.syntax()); 203 let sa = self.analyze(macro_call.syntax());
200 let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); 204 let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call);
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 23af400b8..0ed6d0958 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -95,6 +95,7 @@ impl SourceAnalyzer {
95 } 95 }
96 96
97 fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> { 97 fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> {
98 // FIXME: macros, see `expr_id`
98 let src = InFile { file_id: self.file_id, value: pat }; 99 let src = InFile { file_id: self.file_id, value: pat };
99 self.body_source_map.as_ref()?.node_pat(src) 100 self.body_source_map.as_ref()?.node_pat(src)
100 } 101 }
@@ -167,6 +168,16 @@ impl SourceAnalyzer {
167 Some((struct_field.into(), local)) 168 Some((struct_field.into(), local))
168 } 169 }
169 170
171 pub(crate) fn resolve_record_field_pat(
172 &self,
173 _db: &dyn HirDatabase,
174 field: &ast::RecordFieldPat,
175 ) -> Option<StructField> {
176 let pat_id = self.pat_id(&field.pat()?)?;
177 let struct_field = self.infer.as_ref()?.record_field_pat_resolution(pat_id)?;
178 Some(struct_field.into())
179 }
180
170 pub(crate) fn resolve_macro_call( 181 pub(crate) fn resolve_macro_call(
171 &self, 182 &self,
172 db: &dyn HirDatabase, 183 db: &dyn HirDatabase,
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 82a52804d..0caedd8d8 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -473,16 +473,14 @@ impl ExprCollector<'_> {
473 self.collect_block_items(&block); 473 self.collect_block_items(&block);
474 let statements = block 474 let statements = block
475 .statements() 475 .statements()
476 .filter_map(|s| match s { 476 .map(|s| match s {
477 ast::Stmt::LetStmt(stmt) => { 477 ast::Stmt::LetStmt(stmt) => {
478 let pat = self.collect_pat_opt(stmt.pat()); 478 let pat = self.collect_pat_opt(stmt.pat());
479 let type_ref = stmt.ascribed_type().map(TypeRef::from_ast); 479 let type_ref = stmt.ascribed_type().map(TypeRef::from_ast);
480 let initializer = stmt.initializer().map(|e| self.collect_expr(e)); 480 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
481 Some(Statement::Let { pat, type_ref, initializer }) 481 Statement::Let { pat, type_ref, initializer }
482 }
483 ast::Stmt::ExprStmt(stmt) => {
484 Some(Statement::Expr(self.collect_expr_opt(stmt.expr())))
485 } 482 }
483 ast::Stmt::ExprStmt(stmt) => Statement::Expr(self.collect_expr_opt(stmt.expr())),
486 }) 484 })
487 .collect(); 485 .collect();
488 let tail = block.expr().map(|e| self.collect_expr(e)); 486 let tail = block.expr().map(|e| self.collect_expr(e));
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs
index 4d489f692..5b36a7cc1 100644
--- a/crates/ra_hir_def/src/body/scope.rs
+++ b/crates/ra_hir_def/src/body/scope.rs
@@ -157,6 +157,10 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
157 for arm in arms { 157 for arm in arms {
158 let scope = scopes.new_scope(scope); 158 let scope = scopes.new_scope(scope);
159 scopes.add_bindings(body, scope, arm.pat); 159 scopes.add_bindings(body, scope, arm.pat);
160 if let Some(guard) = arm.guard {
161 scopes.set_scope(guard, scope);
162 compute_expr_scopes(guard, body, scopes, scope);
163 }
160 scopes.set_scope(arm.expr, scope); 164 scopes.set_scope(arm.expr, scope);
161 compute_expr_scopes(arm.expr, body, scopes, scope); 165 compute_expr_scopes(arm.expr, body, scopes, scope);
162 } 166 }
@@ -321,8 +325,11 @@ mod tests {
321 let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap(); 325 let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap();
322 let pat_src = source_map.pat_syntax(resolved.pat()).unwrap(); 326 let pat_src = source_map.pat_syntax(resolved.pat()).unwrap();
323 327
324 let local_name = pat_src.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); 328 let local_name = pat_src.value.either(
325 assert_eq!(local_name.range(), expected_name.syntax().text_range()); 329 |it| it.syntax_node_ptr().to_node(file.syntax()),
330 |it| it.syntax_node_ptr().to_node(file.syntax()),
331 );
332 assert_eq!(local_name.text_range(), expected_name.syntax().text_range());
326 } 333 }
327 334
328 #[test] 335 #[test]
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
index afd538e4a..39b011ad7 100644
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -266,8 +266,8 @@ impl RawItemsCollector {
266 self.add_macro(current_module, it); 266 self.add_macro(current_module, it);
267 return; 267 return;
268 } 268 }
269 ast::ModuleItem::ExternBlock(_) => { 269 ast::ModuleItem::ExternBlock(it) => {
270 // FIXME: add extern block 270 self.add_extern_block(current_module, it);
271 return; 271 return;
272 } 272 }
273 }; 273 };
@@ -278,6 +278,34 @@ impl RawItemsCollector {
278 } 278 }
279 } 279 }
280 280
281 fn add_extern_block(
282 &mut self,
283 current_module: Option<Idx<ModuleData>>,
284 block: ast::ExternBlock,
285 ) {
286 if let Some(items) = block.extern_item_list() {
287 for item in items.extern_items() {
288 let attrs = self.parse_attrs(&item);
289 let visibility =
290 RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
291 let (kind, name) = match item {
292 ast::ExternItem::FnDef(it) => {
293 (DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
294 }
295 ast::ExternItem::StaticDef(it) => {
296 (DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
297 }
298 };
299
300 if let Some(name) = name {
301 let name = name.as_name();
302 let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
303 self.push_item(current_module, attrs, RawItemKind::Def(def));
304 }
305 }
306 }
307 }
308
281 fn add_module(&mut self, current_module: Option<Idx<ModuleData>>, module: ast::Module) { 309 fn add_module(&mut self, current_module: Option<Idx<ModuleData>>, module: ast::Module) {
282 let name = match module.name() { 310 let name = match module.name() {
283 Some(it) => it.as_name(), 311 Some(it) => it.as_name(),
diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs
index 949ca7595..83120fa36 100644
--- a/crates/ra_hir_def/src/nameres/tests.rs
+++ b/crates/ra_hir_def/src/nameres/tests.rs
@@ -25,7 +25,7 @@ fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> {
25#[test] 25#[test]
26fn crate_def_map_smoke_test() { 26fn crate_def_map_smoke_test() {
27 let map = def_map( 27 let map = def_map(
28 " 28 r"
29 //- /lib.rs 29 //- /lib.rs
30 mod foo; 30 mod foo;
31 struct S; 31 struct S;
@@ -45,6 +45,11 @@ fn crate_def_map_smoke_test() {
45 } 45 }
46 46
47 enum E { V } 47 enum E { V }
48
49 extern {
50 static EXT: u8;
51 fn ext();
52 }
48 ", 53 ",
49 ); 54 );
50 assert_snapshot!(map, @r###" 55 assert_snapshot!(map, @r###"
@@ -61,7 +66,9 @@ fn crate_def_map_smoke_test() {
61 ⋮crate::foo::bar 66 ⋮crate::foo::bar
62 ⋮Baz: t v 67 ⋮Baz: t v
63 ⋮E: t 68 ⋮E: t
69 ⋮EXT: v
64 ⋮U: t v 70 ⋮U: t v
71 ⋮ext: v
65 "###) 72 "###)
66} 73}
67 74
diff --git a/crates/ra_hir_expand/Cargo.toml b/crates/ra_hir_expand/Cargo.toml
index d6e3c1f76..2cd522766 100644
--- a/crates/ra_hir_expand/Cargo.toml
+++ b/crates/ra_hir_expand/Cargo.toml
@@ -18,3 +18,4 @@ ra_parser = { path = "../ra_parser" }
18ra_prof = { path = "../ra_prof" } 18ra_prof = { path = "../ra_prof" }
19tt = { path = "../ra_tt", package = "ra_tt" } 19tt = { path = "../ra_tt", package = "ra_tt" }
20mbe = { path = "../ra_mbe", package = "ra_mbe" } 20mbe = { path = "../ra_mbe", package = "ra_mbe" }
21test_utils = { path = "../test_utils"}
diff --git a/crates/ra_hir_expand/src/ast_id_map.rs b/crates/ra_hir_expand/src/ast_id_map.rs
index a3ca302c2..d19569245 100644
--- a/crates/ra_hir_expand/src/ast_id_map.rs
+++ b/crates/ra_hir_expand/src/ast_id_map.rs
@@ -66,7 +66,7 @@ impl AstIdMap {
66 // change parent's id. This means that, say, adding a new function to a 66 // change parent's id. This means that, say, adding a new function to a
67 // trait does not change ids of top-level items, which helps caching. 67 // trait does not change ids of top-level items, which helps caching.
68 bfs(node, |it| { 68 bfs(node, |it| {
69 if let Some(module_item) = ast::ModuleItem::cast(it.clone()) { 69 if let Some(module_item) = ast::ModuleItem::cast(it) {
70 res.alloc(module_item.syntax()); 70 res.alloc(module_item.syntax());
71 } 71 }
72 }); 72 });
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index f9d3787f6..3da137f2e 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -301,7 +301,7 @@ fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Opti
301 } 301 }
302 302
303 // Extern paths ? 303 // Extern paths ?
304 let krate = db.relevant_crates(call_site).get(0)?.clone(); 304 let krate = *db.relevant_crates(call_site).get(0)?;
305 let (extern_source_id, relative_file) = 305 let (extern_source_id, relative_file) =
306 db.crate_graph()[krate].extern_source.extern_path(path)?; 306 db.crate_graph()[krate].extern_source.extern_path(path)?;
307 307
@@ -329,7 +329,7 @@ fn include_expand(
329 329
330 // FIXME: 330 // FIXME:
331 // Handle include as expression 331 // Handle include as expression
332 let res = parse_to_token_tree(&db.file_text(file_id.into())) 332 let res = parse_to_token_tree(&db.file_text(file_id))
333 .ok_or_else(|| mbe::ExpandError::ConversionError)? 333 .ok_or_else(|| mbe::ExpandError::ConversionError)?
334 .0; 334 .0;
335 335
@@ -340,7 +340,7 @@ fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Optio
340 let call_id: MacroCallId = arg_id.into(); 340 let call_id: MacroCallId = arg_id.into();
341 let original_file = call_id.as_file().original_file(db); 341 let original_file = call_id.as_file().original_file(db);
342 342
343 let krate = db.relevant_crates(original_file).get(0)?.clone(); 343 let krate = *db.relevant_crates(original_file).get(0)?;
344 db.crate_graph()[krate].env.get(key) 344 db.crate_graph()[krate].env.get(key)
345} 345}
346 346
@@ -447,7 +447,7 @@ mod tests {
447 file_id: file_id.into(), 447 file_id: file_id.into(),
448 }; 448 };
449 449
450 let id: MacroCallId = db.intern_eager_expansion(eager.into()).into(); 450 let id: MacroCallId = db.intern_eager_expansion(eager).into();
451 id.as_file() 451 id.as_file()
452 } 452 }
453 }; 453 };
diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs
index 97d1208ec..4e0e069c8 100644
--- a/crates/ra_hir_expand/src/proc_macro.rs
+++ b/crates/ra_hir_expand/src/proc_macro.rs
@@ -2,6 +2,7 @@
2 2
3use crate::{db::AstDatabase, LazyMacroId}; 3use crate::{db::AstDatabase, LazyMacroId};
4use ra_db::{CrateId, ProcMacroId}; 4use ra_db::{CrateId, ProcMacroId};
5use tt::buffer::{Cursor, TokenBuffer};
5 6
6#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] 7#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
7pub struct ProcMacroExpander { 8pub struct ProcMacroExpander {
@@ -36,22 +37,107 @@ impl ProcMacroExpander {
36 .clone() 37 .clone()
37 .ok_or_else(|| err!("No derive macro found."))?; 38 .ok_or_else(|| err!("No derive macro found."))?;
38 39
39 let tt = remove_derive_atr(tt, &proc_macro.name) 40 let tt = remove_derive_attrs(tt)
40 .ok_or_else(|| err!("Fail to remove derive for custom derive"))?; 41 .ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
41 42
42 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) 43 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
43 } 44 }
44} 45}
45 46
46fn remove_derive_atr(tt: &tt::Subtree, _name: &str) -> Option<tt::Subtree> { 47fn eat_punct(cursor: &mut Cursor, c: char) -> bool {
47 // FIXME: proper handle the remove derive 48 if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = cursor.token_tree() {
48 // We assume the first 2 tokens are #[derive(name)] 49 if punct.char == c {
49 if tt.token_trees.len() > 2 { 50 *cursor = cursor.bump();
50 let mut tt = tt.clone(); 51 return true;
51 tt.token_trees.remove(0); 52 }
52 tt.token_trees.remove(0);
53 return Some(tt);
54 } 53 }
54 false
55}
56
57fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool {
58 if let Some(tt::TokenTree::Subtree(subtree)) = cursor.token_tree() {
59 if Some(kind) == subtree.delimiter_kind() {
60 *cursor = cursor.bump_subtree();
61 return true;
62 }
63 }
64 false
65}
66
67fn eat_ident(cursor: &mut Cursor, t: &str) -> bool {
68 if let Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) = cursor.token_tree() {
69 if t == ident.text.as_str() {
70 *cursor = cursor.bump();
71 return true;
72 }
73 }
74 false
75}
76
77fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> {
78 let buffer = TokenBuffer::new(&tt.token_trees);
79 let mut p = buffer.begin();
80 let mut result = tt::Subtree::default();
81
82 while !p.eof() {
83 let curr = p;
55 84
56 None 85 if eat_punct(&mut p, '#') {
86 eat_punct(&mut p, '!');
87 let parent = p;
88 if eat_subtree(&mut p, tt::DelimiterKind::Bracket) {
89 if eat_ident(&mut p, "derive") {
90 p = parent.bump();
91 continue;
92 }
93 }
94 }
95
96 result.token_trees.push(curr.token_tree()?.clone());
97 p = curr.bump();
98 }
99
100 Some(result)
101}
102
103#[cfg(test)]
104mod test {
105 use super::*;
106 use test_utils::assert_eq_text;
107
108 #[test]
109 fn test_remove_derive_attrs() {
110 let tt = mbe::parse_to_token_tree(
111 r#"
112 #[allow(unused)]
113 #[derive(Copy)]
114 #[derive(Hello)]
115 struct A {
116 bar: u32
117 }
118"#,
119 )
120 .unwrap()
121 .0;
122 let result = format!("{:#?}", remove_derive_attrs(&tt).unwrap());
123
124 assert_eq_text!(
125 &result,
126 r#"
127SUBTREE $
128 PUNCH # [alone] 0
129 SUBTREE [] 1
130 IDENT allow 2
131 SUBTREE () 3
132 IDENT unused 4
133 IDENT struct 15
134 IDENT A 16
135 SUBTREE {} 17
136 IDENT bar 18
137 PUNCH : [alone] 19
138 IDENT u32 20
139"#
140 .trim()
141 );
142 }
57} 143}
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 177bdbcb0..04d3cd6a2 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 = "28cef6ff403d403e6ad2f3d27d944e9ffac1bce8" } 30chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" }
31chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "28cef6ff403d403e6ad2f3d27d944e9ffac1bce8" } 31chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" }
32chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "28cef6ff403d403e6ad2f3d27d944e9ffac1bce8" } 32chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" }
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 688026a04..779e78574 100644
--- a/crates/ra_hir_ty/src/_match.rs
+++ b/crates/ra_hir_ty/src/_match.rs
@@ -235,10 +235,19 @@ impl From<PatId> for PatIdOrWild {
235 } 235 }
236} 236}
237 237
238impl From<&PatId> for PatIdOrWild {
239 fn from(pat_id: &PatId) -> Self {
240 Self::PatId(*pat_id)
241 }
242}
243
238#[derive(Debug, Clone, Copy, PartialEq)] 244#[derive(Debug, Clone, Copy, PartialEq)]
239pub enum MatchCheckErr { 245pub enum MatchCheckErr {
240 NotImplemented, 246 NotImplemented,
241 MalformedMatchArm, 247 MalformedMatchArm,
248 /// Used when type inference cannot resolve the type of
249 /// a pattern or expression.
250 Unknown,
242} 251}
243 252
244/// The return type of `is_useful` is either an indication of usefulness 253/// The return type of `is_useful` is either an indication of usefulness
@@ -290,10 +299,14 @@ impl PatStack {
290 Self::from_slice(&self.0[1..]) 299 Self::from_slice(&self.0[1..])
291 } 300 }
292 301
293 fn replace_head_with<T: Into<PatIdOrWild> + Copy>(&self, pat_ids: &[T]) -> PatStack { 302 fn replace_head_with<I, T>(&self, pats: I) -> PatStack
303 where
304 I: Iterator<Item = T>,
305 T: Into<PatIdOrWild>,
306 {
294 let mut patterns: PatStackInner = smallvec![]; 307 let mut patterns: PatStackInner = smallvec![];
295 for pat in pat_ids { 308 for pat in pats {
296 patterns.push((*pat).into()); 309 patterns.push(pat.into());
297 } 310 }
298 for pat in &self.0[1..] { 311 for pat in &self.0[1..] {
299 patterns.push(*pat); 312 patterns.push(*pat);
@@ -330,7 +343,7 @@ impl PatStack {
330 return Err(MatchCheckErr::NotImplemented); 343 return Err(MatchCheckErr::NotImplemented);
331 } 344 }
332 345
333 Some(self.replace_head_with(pat_ids)) 346 Some(self.replace_head_with(pat_ids.iter()))
334 } 347 }
335 (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => { 348 (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => {
336 match cx.body.exprs[lit_expr] { 349 match cx.body.exprs[lit_expr] {
@@ -382,7 +395,7 @@ impl PatStack {
382 new_patterns.push((*pat_id).into()); 395 new_patterns.push((*pat_id).into());
383 } 396 }
384 397
385 Some(self.replace_head_with(&new_patterns)) 398 Some(self.replace_head_with(new_patterns.into_iter()))
386 } else { 399 } else {
387 return Err(MatchCheckErr::MalformedMatchArm); 400 return Err(MatchCheckErr::MalformedMatchArm);
388 } 401 }
@@ -390,13 +403,41 @@ impl PatStack {
390 // If there is no ellipsis in the tuple pattern, the number 403 // If there is no ellipsis in the tuple pattern, the number
391 // of patterns must equal the constructor arity. 404 // of patterns must equal the constructor arity.
392 if pat_ids.len() == constructor_arity { 405 if pat_ids.len() == constructor_arity {
393 Some(self.replace_head_with(pat_ids)) 406 Some(self.replace_head_with(pat_ids.into_iter()))
394 } else { 407 } else {
395 return Err(MatchCheckErr::MalformedMatchArm); 408 return Err(MatchCheckErr::MalformedMatchArm);
396 } 409 }
397 } 410 }
398 } 411 }
399 } 412 }
413 (Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => {
414 let pat_id = self.head().as_id().expect("we know this isn't a wild");
415 if !enum_variant_matches(cx, pat_id, *e) {
416 None
417 } else {
418 match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() {
419 VariantData::Record(struct_field_arena) => {
420 // Here we treat any missing fields in the record as the wild pattern, as
421 // if the record has ellipsis. We want to do this here even if the
422 // record does not contain ellipsis, because it allows us to continue
423 // enforcing exhaustiveness for the rest of the match statement.
424 //
425 // Creating the diagnostic for the missing field in the pattern
426 // should be done in a different diagnostic.
427 let patterns = struct_field_arena.iter().map(|(_, struct_field)| {
428 arg_patterns
429 .iter()
430 .find(|pat| pat.name == struct_field.name)
431 .map(|pat| PatIdOrWild::from(pat.pat))
432 .unwrap_or(PatIdOrWild::Wild)
433 });
434
435 Some(self.replace_head_with(patterns))
436 }
437 _ => return Err(MatchCheckErr::Unknown),
438 }
439 }
440 }
400 (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented), 441 (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented),
401 (_, _) => return Err(MatchCheckErr::NotImplemented), 442 (_, _) => return Err(MatchCheckErr::NotImplemented),
402 }; 443 };
@@ -655,8 +696,8 @@ impl Constructor {
655 Constructor::Enum(e) => { 696 Constructor::Enum(e) => {
656 match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() { 697 match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() {
657 VariantData::Tuple(struct_field_data) => struct_field_data.len(), 698 VariantData::Tuple(struct_field_data) => struct_field_data.len(),
699 VariantData::Record(struct_field_data) => struct_field_data.len(),
658 VariantData::Unit => 0, 700 VariantData::Unit => 0,
659 _ => return Err(MatchCheckErr::NotImplemented),
660 } 701 }
661 } 702 }
662 }; 703 };
@@ -695,10 +736,10 @@ fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Opt
695 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), 736 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)),
696 _ => return Err(MatchCheckErr::NotImplemented), 737 _ => return Err(MatchCheckErr::NotImplemented),
697 }, 738 },
698 Pat::TupleStruct { .. } | Pat::Path(_) => { 739 Pat::TupleStruct { .. } | Pat::Path(_) | Pat::Record { .. } => {
699 let pat_id = pat.as_id().expect("we already know this pattern is not a wild"); 740 let pat_id = pat.as_id().expect("we already know this pattern is not a wild");
700 let variant_id = 741 let variant_id =
701 cx.infer.variant_resolution_for_pat(pat_id).ok_or(MatchCheckErr::NotImplemented)?; 742 cx.infer.variant_resolution_for_pat(pat_id).ok_or(MatchCheckErr::Unknown)?;
702 match variant_id { 743 match variant_id {
703 VariantId::EnumVariantId(enum_variant_id) => { 744 VariantId::EnumVariantId(enum_variant_id) => {
704 Some(Constructor::Enum(enum_variant_id)) 745 Some(Constructor::Enum(enum_variant_id))
@@ -759,20 +800,22 @@ mod tests {
759 pub(super) use insta::assert_snapshot; 800 pub(super) use insta::assert_snapshot;
760 pub(super) use ra_db::fixture::WithFixture; 801 pub(super) use ra_db::fixture::WithFixture;
761 802
762 pub(super) use crate::test_db::TestDB; 803 pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB};
763 804
764 pub(super) fn check_diagnostic_message(content: &str) -> String { 805 pub(super) fn check_diagnostic_message(content: &str) -> String {
765 TestDB::with_single_file(content).0.diagnostics().0 806 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().0
766 } 807 }
767 808
768 pub(super) fn check_diagnostic(content: &str) { 809 pub(super) fn check_diagnostic(content: &str) {
769 let diagnostic_count = TestDB::with_single_file(content).0.diagnostics().1; 810 let diagnostic_count =
811 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().1;
770 812
771 assert_eq!(1, diagnostic_count, "no diagnostic reported"); 813 assert_eq!(1, diagnostic_count, "no diagnostic reported");
772 } 814 }
773 815
774 pub(super) fn check_no_diagnostic(content: &str) { 816 pub(super) fn check_no_diagnostic(content: &str) {
775 let diagnostic_count = TestDB::with_single_file(content).0.diagnostics().1; 817 let diagnostic_count =
818 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().1;
776 819
777 assert_eq!(0, diagnostic_count, "expected no diagnostic, found one"); 820 assert_eq!(0, diagnostic_count, "expected no diagnostic, found one");
778 } 821 }
@@ -1532,6 +1575,236 @@ mod tests {
1532 } 1575 }
1533 1576
1534 #[test] 1577 #[test]
1578 fn enum_record_no_arms() {
1579 let content = r"
1580 enum Either {
1581 A { foo: bool },
1582 B,
1583 }
1584 fn test_fn() {
1585 let a = Either::A { foo: true };
1586 match a {
1587 }
1588 }
1589 ";
1590
1591 check_diagnostic(content);
1592 }
1593
1594 #[test]
1595 fn enum_record_missing_arms() {
1596 let content = r"
1597 enum Either {
1598 A { foo: bool },
1599 B,
1600 }
1601 fn test_fn() {
1602 let a = Either::A { foo: true };
1603 match a {
1604 Either::A { foo: true } => (),
1605 }
1606 }
1607 ";
1608
1609 check_diagnostic(content);
1610 }
1611
1612 #[test]
1613 fn enum_record_no_diagnostic() {
1614 let content = r"
1615 enum Either {
1616 A { foo: bool },
1617 B,
1618 }
1619 fn test_fn() {
1620 let a = Either::A { foo: true };
1621 match a {
1622 Either::A { foo: true } => (),
1623 Either::A { foo: false } => (),
1624 Either::B => (),
1625 }
1626 }
1627 ";
1628
1629 check_no_diagnostic(content);
1630 }
1631
1632 #[test]
1633 fn enum_record_missing_field_no_diagnostic() {
1634 let content = r"
1635 enum Either {
1636 A { foo: bool },
1637 B,
1638 }
1639 fn test_fn() {
1640 let a = Either::B;
1641 match a {
1642 Either::A { } => (),
1643 Either::B => (),
1644 }
1645 }
1646 ";
1647
1648 // When `Either::A` is missing a struct member, we don't want
1649 // to fire the missing match arm diagnostic. This should fire
1650 // some other diagnostic.
1651 check_no_diagnostic(content);
1652 }
1653
1654 #[test]
1655 fn enum_record_missing_field_missing_match_arm() {
1656 let content = r"
1657 enum Either {
1658 A { foo: bool },
1659 B,
1660 }
1661 fn test_fn() {
1662 let a = Either::B;
1663 match a {
1664 Either::A { } => (),
1665 }
1666 }
1667 ";
1668
1669 // Even though `Either::A` is missing fields, we still want to fire
1670 // the missing arm diagnostic here, since we know `Either::B` is missing.
1671 check_diagnostic(content);
1672 }
1673
1674 #[test]
1675 fn enum_record_no_diagnostic_wild() {
1676 let content = r"
1677 enum Either {
1678 A { foo: bool },
1679 B,
1680 }
1681 fn test_fn() {
1682 let a = Either::A { foo: true };
1683 match a {
1684 Either::A { foo: _ } => (),
1685 Either::B => (),
1686 }
1687 }
1688 ";
1689
1690 check_no_diagnostic(content);
1691 }
1692
1693 #[test]
1694 fn enum_record_fields_out_of_order_missing_arm() {
1695 let content = r"
1696 enum Either {
1697 A { foo: bool, bar: () },
1698 B,
1699 }
1700 fn test_fn() {
1701 let a = Either::A { foo: true };
1702 match a {
1703 Either::A { bar: (), foo: false } => (),
1704 Either::A { foo: true, bar: () } => (),
1705 }
1706 }
1707 ";
1708
1709 check_diagnostic(content);
1710 }
1711
1712 #[test]
1713 fn enum_record_fields_out_of_order_no_diagnostic() {
1714 let content = r"
1715 enum Either {
1716 A { foo: bool, bar: () },
1717 B,
1718 }
1719 fn test_fn() {
1720 let a = Either::A { foo: true };
1721 match a {
1722 Either::A { bar: (), foo: false } => (),
1723 Either::A { foo: true, bar: () } => (),
1724 Either::B => (),
1725 }
1726 }
1727 ";
1728
1729 check_no_diagnostic(content);
1730 }
1731
1732 #[test]
1733 fn enum_record_ellipsis_missing_arm() {
1734 let content = r"
1735 enum Either {
1736 A { foo: bool, bar: bool },
1737 B,
1738 }
1739 fn test_fn() {
1740 match Either::B {
1741 Either::A { foo: true, .. } => (),
1742 Either::B => (),
1743 }
1744 }
1745 ";
1746
1747 check_diagnostic(content);
1748 }
1749
1750 #[test]
1751 fn enum_record_ellipsis_no_diagnostic() {
1752 let content = r"
1753 enum Either {
1754 A { foo: bool, bar: bool },
1755 B,
1756 }
1757 fn test_fn() {
1758 let a = Either::A { foo: true };
1759 match a {
1760 Either::A { foo: true, .. } => (),
1761 Either::A { foo: false, .. } => (),
1762 Either::B => (),
1763 }
1764 }
1765 ";
1766
1767 check_no_diagnostic(content);
1768 }
1769
1770 #[test]
1771 fn enum_record_ellipsis_all_fields_missing_arm() {
1772 let content = r"
1773 enum Either {
1774 A { foo: bool, bar: bool },
1775 B,
1776 }
1777 fn test_fn() {
1778 let a = Either::B;
1779 match a {
1780 Either::A { .. } => (),
1781 }
1782 }
1783 ";
1784
1785 check_diagnostic(content);
1786 }
1787
1788 #[test]
1789 fn enum_record_ellipsis_all_fields_no_diagnostic() {
1790 let content = r"
1791 enum Either {
1792 A { foo: bool, bar: bool },
1793 B,
1794 }
1795 fn test_fn() {
1796 let a = Either::B;
1797 match a {
1798 Either::A { .. } => (),
1799 Either::B => (),
1800 }
1801 }
1802 ";
1803
1804 check_no_diagnostic(content);
1805 }
1806
1807 #[test]
1535 fn enum_tuple_partial_ellipsis_no_diagnostic() { 1808 fn enum_tuple_partial_ellipsis_no_diagnostic() {
1536 let content = r" 1809 let content = r"
1537 enum Either { 1810 enum Either {
@@ -1689,25 +1962,6 @@ mod false_negatives {
1689 } 1962 }
1690 1963
1691 #[test] 1964 #[test]
1692 fn enum_record() {
1693 let content = r"
1694 enum Either {
1695 A { foo: u32 },
1696 B,
1697 }
1698 fn test_fn() {
1699 match Either::B {
1700 Either::A { foo: 5 } => (),
1701 }
1702 }
1703 ";
1704
1705 // This is a false negative.
1706 // We don't currently handle enum record types.
1707 check_no_diagnostic(content);
1708 }
1709
1710 #[test]
1711 fn internal_or() { 1965 fn internal_or() {
1712 let content = r" 1966 let content = r"
1713 fn test_fn() { 1967 fn test_fn() {
@@ -1796,4 +2050,22 @@ mod false_negatives {
1796 // We don't currently handle tuple patterns with ellipsis. 2050 // We don't currently handle tuple patterns with ellipsis.
1797 check_no_diagnostic(content); 2051 check_no_diagnostic(content);
1798 } 2052 }
2053
2054 #[test]
2055 fn struct_missing_arm() {
2056 let content = r"
2057 struct Foo {
2058 a: bool,
2059 }
2060 fn test_fn(f: Foo) {
2061 match f {
2062 Foo { a: true } => {},
2063 }
2064 }
2065 ";
2066
2067 // This is a false negative.
2068 // We don't currently handle structs.
2069 check_no_diagnostic(content);
2070 }
1799} 2071}
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs
index 33da16b48..9e5dfeab3 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -107,6 +107,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
107 krate: CrateId, 107 krate: CrateId,
108 goal: crate::Canonical<crate::InEnvironment<crate::Obligation>>, 108 goal: crate::Canonical<crate::InEnvironment<crate::Obligation>>,
109 ) -> Option<crate::traits::Solution>; 109 ) -> Option<crate::traits::Solution>;
110
111 #[salsa::invoke(crate::traits::chalk::program_clauses_for_chalk_env_query)]
112 fn program_clauses_for_chalk_env(
113 &self,
114 krate: CrateId,
115 env: chalk_ir::Environment<chalk::Interner>,
116 ) -> chalk_ir::ProgramClauses<chalk::Interner>;
110} 117}
111 118
112fn infer_wait(db: &impl HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> { 119fn infer_wait(db: &impl HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index b6d9b3438..dfb6a435f 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -127,6 +127,7 @@ pub struct InferenceResult {
127 field_resolutions: FxHashMap<ExprId, StructFieldId>, 127 field_resolutions: FxHashMap<ExprId, StructFieldId>,
128 /// For each field in record literal, records the field it resolves to. 128 /// For each field in record literal, records the field it resolves to.
129 record_field_resolutions: FxHashMap<ExprId, StructFieldId>, 129 record_field_resolutions: FxHashMap<ExprId, StructFieldId>,
130 record_field_pat_resolutions: FxHashMap<PatId, StructFieldId>,
130 /// For each struct literal, records the variant it resolves to. 131 /// For each struct literal, records the variant it resolves to.
131 variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, 132 variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
132 /// For each associated item record what it resolves to 133 /// For each associated item record what it resolves to
@@ -147,6 +148,9 @@ impl InferenceResult {
147 pub fn record_field_resolution(&self, expr: ExprId) -> Option<StructFieldId> { 148 pub fn record_field_resolution(&self, expr: ExprId) -> Option<StructFieldId> {
148 self.record_field_resolutions.get(&expr).copied() 149 self.record_field_resolutions.get(&expr).copied()
149 } 150 }
151 pub fn record_field_pat_resolution(&self, pat: PatId) -> Option<StructFieldId> {
152 self.record_field_pat_resolutions.get(&pat).copied()
153 }
150 pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> { 154 pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> {
151 self.variant_resolutions.get(&id.into()).copied() 155 self.variant_resolutions.get(&id.into()).copied()
152 } 156 }
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs
index 8ec4d4ace..7c2ad4384 100644
--- a/crates/ra_hir_ty/src/infer/pat.rs
+++ b/crates/ra_hir_ty/src/infer/pat.rs
@@ -7,6 +7,7 @@ use hir_def::{
7 expr::{BindingAnnotation, Pat, PatId, RecordFieldPat}, 7 expr::{BindingAnnotation, Pat, PatId, RecordFieldPat},
8 path::Path, 8 path::Path,
9 type_ref::Mutability, 9 type_ref::Mutability,
10 StructFieldId,
10}; 11};
11use hir_expand::name::Name; 12use hir_expand::name::Name;
12use test_utils::tested_by; 13use test_utils::tested_by;
@@ -67,6 +68,11 @@ impl<'a> InferenceContext<'a> {
67 let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); 68 let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
68 for subpat in subpats { 69 for subpat in subpats {
69 let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name)); 70 let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name));
71 if let Some(local_id) = matching_field {
72 let field_def = StructFieldId { parent: def.unwrap(), local_id };
73 self.result.record_field_pat_resolutions.insert(subpat.pat, field_def);
74 }
75
70 let expected_ty = 76 let expected_ty =
71 matching_field.map_or(Ty::Unknown, |field| field_tys[field].clone().subst(&substs)); 77 matching_field.map_or(Ty::Unknown, |field| field_tys[field].clone().subst(&substs));
72 let expected_ty = self.normalize_associated_types_in(expected_ty); 78 let expected_ty = self.normalize_associated_types_in(expected_ty);
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index a4b8d6683..279c06d65 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -863,7 +863,7 @@ pub trait TypeWalk {
863 &mut |ty, binders| { 863 &mut |ty, binders| {
864 if let &mut Ty::Bound(bound) = ty { 864 if let &mut Ty::Bound(bound) = ty {
865 if bound.debruijn >= binders { 865 if bound.debruijn >= binders {
866 *ty = substs.0[bound.index].clone(); 866 *ty = substs.0[bound.index].clone().shift_bound_vars(binders);
867 } 867 }
868 } 868 }
869 }, 869 },
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs
index 3a4d58bf9..8498d3d96 100644
--- a/crates/ra_hir_ty/src/test_db.rs
+++ b/crates/ra_hir_ty/src/test_db.rs
@@ -12,7 +12,7 @@ use ra_db::{
12}; 12};
13use stdx::format_to; 13use stdx::format_to;
14 14
15use crate::{db::HirDatabase, expr::ExprValidator}; 15use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator};
16 16
17#[salsa::database( 17#[salsa::database(
18 ra_db::SourceDatabaseExtStorage, 18 ra_db::SourceDatabaseExtStorage,
@@ -104,10 +104,7 @@ impl TestDB {
104 panic!("Can't find module for file") 104 panic!("Can't find module for file")
105 } 105 }
106 106
107 // FIXME: don't duplicate this 107 fn diag<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
108 pub fn diagnostics(&self) -> (String, u32) {
109 let mut buf = String::new();
110 let mut count = 0;
111 let crate_graph = self.crate_graph(); 108 let crate_graph = self.crate_graph();
112 for krate in crate_graph.iter() { 109 for krate in crate_graph.iter() {
113 let crate_def_map = self.crate_def_map(krate); 110 let crate_def_map = self.crate_def_map(krate);
@@ -132,15 +129,36 @@ impl TestDB {
132 129
133 for f in fns { 130 for f in fns {
134 let infer = self.infer(f.into()); 131 let infer = self.infer(f.into());
135 let mut sink = DiagnosticSink::new(|d| { 132 let mut sink = DiagnosticSink::new(&mut cb);
136 format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message());
137 count += 1;
138 });
139 infer.add_diagnostics(self, f, &mut sink); 133 infer.add_diagnostics(self, f, &mut sink);
140 let mut validator = ExprValidator::new(f, infer, &mut sink); 134 let mut validator = ExprValidator::new(f, infer, &mut sink);
141 validator.validate_body(self); 135 validator.validate_body(self);
142 } 136 }
143 } 137 }
138 }
139
140 pub fn diagnostics(&self) -> (String, u32) {
141 let mut buf = String::new();
142 let mut count = 0;
143 self.diag(|d| {
144 format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message());
145 count += 1;
146 });
147 (buf, count)
148 }
149
150 /// Like `diagnostics`, but filtered for a single diagnostic.
151 pub fn diagnostic<D: Diagnostic>(&self) -> (String, u32) {
152 let mut buf = String::new();
153 let mut count = 0;
154 self.diag(|d| {
155 // We want to filter diagnostics by the particular one we are testing for, to
156 // avoid surprising results in tests.
157 if d.downcast_ref::<D>().is_some() {
158 format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message());
159 count += 1;
160 };
161 });
144 (buf, count) 162 (buf, count)
145 } 163 }
146} 164}
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 81fc0f63a..846005baa 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -18,16 +18,19 @@ use hir_def::{
18 nameres::CrateDefMap, 18 nameres::CrateDefMap,
19 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, 19 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
20}; 20};
21use hir_expand::InFile; 21use hir_expand::{db::AstDatabase, InFile};
22use insta::assert_snapshot; 22use insta::assert_snapshot;
23use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase}; 23use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase};
24use ra_syntax::{ 24use ra_syntax::{
25 algo, 25 algo,
26 ast::{self, AstNode}, 26 ast::{self, AstNode},
27 SyntaxNode,
27}; 28};
28use stdx::format_to; 29use stdx::format_to;
29 30
30use crate::{db::HirDatabase, display::HirDisplay, test_db::TestDB, InferenceResult}; 31use crate::{
32 db::HirDatabase, display::HirDisplay, infer::TypeMismatch, test_db::TestDB, InferenceResult, Ty,
33};
31 34
32// These tests compare the inference results for all expressions in a file 35// These tests compare the inference results for all expressions in a file
33// against snapshots of the expected results using insta. Use cargo-insta to 36// against snapshots of the expected results using insta. Use cargo-insta to
@@ -67,13 +70,19 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
67 70
68 let mut infer_def = |inference_result: Arc<InferenceResult>, 71 let mut infer_def = |inference_result: Arc<InferenceResult>,
69 body_source_map: Arc<BodySourceMap>| { 72 body_source_map: Arc<BodySourceMap>| {
70 let mut types = Vec::new(); 73 let mut types: Vec<(InFile<SyntaxNode>, &Ty)> = Vec::new();
71 let mut mismatches = Vec::new(); 74 let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new();
72 75
73 for (pat, ty) in inference_result.type_of_pat.iter() { 76 for (pat, ty) in inference_result.type_of_pat.iter() {
74 let syntax_ptr = match body_source_map.pat_syntax(pat) { 77 let syntax_ptr = match body_source_map.pat_syntax(pat) {
75 Ok(sp) => { 78 Ok(sp) => {
76 sp.map(|ast| ast.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr())) 79 let root = db.parse_or_expand(sp.file_id).unwrap();
80 sp.map(|ptr| {
81 ptr.either(
82 |it| it.to_node(&root).syntax().clone(),
83 |it| it.to_node(&root).syntax().clone(),
84 )
85 })
77 } 86 }
78 Err(SyntheticSyntax) => continue, 87 Err(SyntheticSyntax) => continue,
79 }; 88 };
@@ -81,29 +90,31 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
81 } 90 }
82 91
83 for (expr, ty) in inference_result.type_of_expr.iter() { 92 for (expr, ty) in inference_result.type_of_expr.iter() {
84 let syntax_ptr = match body_source_map.expr_syntax(expr) { 93 let node = match body_source_map.expr_syntax(expr) {
85 Ok(sp) => sp.map(|ast| ast.syntax_node_ptr()), 94 Ok(sp) => {
95 let root = db.parse_or_expand(sp.file_id).unwrap();
96 sp.map(|ptr| ptr.to_node(&root).syntax().clone())
97 }
86 Err(SyntheticSyntax) => continue, 98 Err(SyntheticSyntax) => continue,
87 }; 99 };
88 types.push((syntax_ptr.clone(), ty)); 100 types.push((node.clone(), ty));
89 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) { 101 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) {
90 mismatches.push((syntax_ptr, mismatch)); 102 mismatches.push((node, mismatch));
91 } 103 }
92 } 104 }
93 105
94 // sort ranges for consistency 106 // sort ranges for consistency
95 types.sort_by_key(|(src_ptr, _)| { 107 types.sort_by_key(|(node, _)| {
96 (src_ptr.value.range().start(), src_ptr.value.range().end()) 108 let range = node.value.text_range();
109 (range.start(), range.end())
97 }); 110 });
98 for (src_ptr, ty) in &types { 111 for (node, ty) in &types {
99 let node = src_ptr.value.to_node(&src_ptr.file_syntax(&db)); 112 let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.value.clone()) {
100
101 let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.clone()) {
102 (self_param.self_token().unwrap().text_range(), "self".to_string()) 113 (self_param.self_token().unwrap().text_range(), "self".to_string())
103 } else { 114 } else {
104 (src_ptr.value.range(), node.text().to_string().replace("\n", " ")) 115 (node.value.text_range(), node.value.text().to_string().replace("\n", " "))
105 }; 116 };
106 let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" }; 117 let macro_prefix = if node.file_id != file_id.into() { "!" } else { "" };
107 format_to!( 118 format_to!(
108 buf, 119 buf,
109 "{}{} '{}': {}\n", 120 "{}{} '{}': {}\n",
@@ -114,11 +125,12 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
114 ); 125 );
115 } 126 }
116 if include_mismatches { 127 if include_mismatches {
117 mismatches.sort_by_key(|(src_ptr, _)| { 128 mismatches.sort_by_key(|(node, _)| {
118 (src_ptr.value.range().start(), src_ptr.value.range().end()) 129 let range = node.value.text_range();
130 (range.start(), range.end())
119 }); 131 });
120 for (src_ptr, mismatch) in &mismatches { 132 for (src_ptr, mismatch) in &mismatches {
121 let range = src_ptr.value.range(); 133 let range = src_ptr.value.text_range();
122 let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" }; 134 let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" };
123 format_to!( 135 format_to!(
124 buf, 136 buf,
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs
index 07cbc521a..6ea51d5d3 100644
--- a/crates/ra_hir_ty/src/tests/patterns.rs
+++ b/crates/ra_hir_ty/src/tests/patterns.rs
@@ -455,3 +455,29 @@ fn test() {
455 "### 455 "###
456 ); 456 );
457} 457}
458
459#[test]
460fn infer_guard() {
461 assert_snapshot!(
462 infer(r#"
463struct S;
464impl S { fn foo(&self) -> bool { false } }
465
466fn main() {
467 match S {
468 s if s.foo() => (),
469 }
470}
471 "#), @"
472 [28; 32) 'self': &S
473 [42; 51) '{ false }': bool
474 [44; 49) 'false': bool
475 [65; 116) '{ ... } }': ()
476 [71; 114) 'match ... }': ()
477 [77; 78) 'S': S
478 [89; 90) 's': S
479 [94; 95) 's': S
480 [94; 101) 's.foo()': bool
481 [105; 107) '()': ()
482 ")
483}
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index 61284d672..61a6801fc 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -533,3 +533,44 @@ fn foo(b: Bar) {
533 "### 533 "###
534 ); 534 );
535} 535}
536
537#[test]
538fn issue_4053_diesel_where_clauses() {
539 assert_snapshot!(
540 infer(r#"
541trait BoxedDsl<DB> {
542 type Output;
543 fn internal_into_boxed(self) -> Self::Output;
544}
545
546struct SelectStatement<From, Select, Distinct, Where, Order, LimitOffset, GroupBy, Locking> {
547 order: Order,
548}
549
550trait QueryFragment<DB: Backend> {}
551
552trait Into<T> { fn into(self) -> T; }
553
554impl<F, S, D, W, O, LOf, DB> BoxedDsl<DB>
555 for SelectStatement<F, S, D, W, O, LOf, G>
556where
557 O: Into<dyn QueryFragment<DB>>,
558{
559 type Output = XXX;
560
561 fn internal_into_boxed(self) -> Self::Output {
562 self.order.into();
563 }
564}
565"#),
566 @r###"
567 [66; 70) 'self': Self
568 [268; 272) 'self': Self
569 [467; 471) 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}>
570 [489; 523) '{ ... }': ()
571 [499; 503) 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}>
572 [499; 509) 'self.order': O
573 [499; 516) 'self.o...into()': dyn QueryFragment<DB>
574 "###
575 );
576}
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs
index 05791a848..6bc6d474c 100644
--- a/crates/ra_hir_ty/src/traits.rs
+++ b/crates/ra_hir_ty/src/traits.rs
@@ -225,7 +225,7 @@ fn solution_from_chalk(
225 None => unimplemented!(), 225 None => unimplemented!(),
226 }) 226 })
227 .collect(); 227 .collect();
228 let result = Canonical { value, num_vars: subst.binders.len() }; 228 let result = Canonical { value, num_vars: subst.binders.len(&Interner) };
229 SolutionVariables(result) 229 SolutionVariables(result)
230 }; 230 };
231 match solution { 231 match solution {
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index e00a82db2..1ccb7c3b4 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -4,8 +4,8 @@ use std::{fmt, sync::Arc};
4use log::debug; 4use log::debug;
5 5
6use chalk_ir::{ 6use chalk_ir::{
7 cast::Cast, fold::shift::Shift, Goal, GoalData, Parameter, PlaceholderIndex, TypeName, 7 cast::Cast, fold::shift::Shift, interner::HasInterner, Goal, GoalData, Parameter,
8 UniverseIndex, 8 PlaceholderIndex, TypeName, UniverseIndex,
9}; 9};
10 10
11use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId}; 11use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId};
@@ -33,8 +33,10 @@ impl chalk_ir::interner::Interner for Interner {
33 type InternedGoals = Vec<Goal<Self>>; 33 type InternedGoals = Vec<Goal<Self>>;
34 type InternedSubstitution = Vec<Parameter<Self>>; 34 type InternedSubstitution = Vec<Parameter<Self>>;
35 type InternedProgramClause = chalk_ir::ProgramClauseData<Self>; 35 type InternedProgramClause = chalk_ir::ProgramClauseData<Self>;
36 type InternedProgramClauses = Vec<chalk_ir::ProgramClause<Self>>; 36 type InternedProgramClauses = Arc<[chalk_ir::ProgramClause<Self>]>;
37 type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>; 37 type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>;
38 type InternedParameterKinds = Vec<chalk_ir::ParameterKind<()>>;
39 type InternedCanonicalVarKinds = Vec<chalk_ir::ParameterKind<UniverseIndex>>;
38 type Identifier = TypeAliasId; 40 type Identifier = TypeAliasId;
39 type DefId = InternId; 41 type DefId = InternId;
40 42
@@ -60,6 +62,27 @@ impl chalk_ir::interner::Interner for Interner {
60 tls::with_current_program(|prog| Some(prog?.debug_alias(alias, fmt))) 62 tls::with_current_program(|prog| Some(prog?.debug_alias(alias, fmt)))
61 } 63 }
62 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
63 fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { 86 fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
64 tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt))) 87 tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt)))
65 } 88 }
@@ -202,15 +225,15 @@ impl chalk_ir::interner::Interner for Interner {
202 fn intern_program_clauses( 225 fn intern_program_clauses(
203 &self, 226 &self,
204 data: impl IntoIterator<Item = chalk_ir::ProgramClause<Self>>, 227 data: impl IntoIterator<Item = chalk_ir::ProgramClause<Self>>,
205 ) -> Vec<chalk_ir::ProgramClause<Self>> { 228 ) -> Arc<[chalk_ir::ProgramClause<Self>]> {
206 data.into_iter().collect() 229 data.into_iter().collect()
207 } 230 }
208 231
209 fn program_clauses_data<'a>( 232 fn program_clauses_data<'a>(
210 &self, 233 &self,
211 clauses: &'a Vec<chalk_ir::ProgramClause<Self>>, 234 clauses: &'a Arc<[chalk_ir::ProgramClause<Self>]>,
212 ) -> &'a [chalk_ir::ProgramClause<Self>] { 235 ) -> &'a [chalk_ir::ProgramClause<Self>] {
213 clauses 236 &clauses
214 } 237 }
215 238
216 fn intern_quantified_where_clauses( 239 fn intern_quantified_where_clauses(
@@ -226,6 +249,34 @@ impl chalk_ir::interner::Interner for Interner {
226 ) -> &'a [chalk_ir::QuantifiedWhereClause<Self>] { 249 ) -> &'a [chalk_ir::QuantifiedWhereClause<Self>] {
227 clauses 250 clauses
228 } 251 }
252
253 fn intern_parameter_kinds(
254 &self,
255 data: impl IntoIterator<Item = chalk_ir::ParameterKind<()>>,
256 ) -> Self::InternedParameterKinds {
257 data.into_iter().collect()
258 }
259
260 fn parameter_kinds_data<'a>(
261 &self,
262 parameter_kinds: &'a Self::InternedParameterKinds,
263 ) -> &'a [chalk_ir::ParameterKind<()>] {
264 &parameter_kinds
265 }
266
267 fn intern_canonical_var_kinds(
268 &self,
269 data: impl IntoIterator<Item = chalk_ir::ParameterKind<UniverseIndex>>,
270 ) -> Self::InternedCanonicalVarKinds {
271 data.into_iter().collect()
272 }
273
274 fn canonical_var_kinds_data<'a>(
275 &self,
276 canonical_var_kinds: &'a Self::InternedCanonicalVarKinds,
277 ) -> &'a [chalk_ir::ParameterKind<UniverseIndex>] {
278 &canonical_var_kinds
279 }
229} 280}
230 281
231impl chalk_ir::interner::HasInterner for Interner { 282impl chalk_ir::interner::HasInterner for Interner {
@@ -268,9 +319,12 @@ impl ToChalk for Ty {
268 Ty::Projection(proj_ty) => { 319 Ty::Projection(proj_ty) => {
269 let associated_ty_id = proj_ty.associated_ty.to_chalk(db); 320 let associated_ty_id = proj_ty.associated_ty.to_chalk(db);
270 let substitution = proj_ty.parameters.to_chalk(db); 321 let substitution = proj_ty.parameters.to_chalk(db);
271 chalk_ir::AliasTy { associated_ty_id, substitution } 322 chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
272 .cast(&Interner) 323 associated_ty_id,
273 .intern(&Interner) 324 substitution,
325 })
326 .cast(&Interner)
327 .intern(&Interner)
274 } 328 }
275 Ty::Placeholder(id) => { 329 Ty::Placeholder(id) => {
276 let interned_id = db.intern_type_param_id(id); 330 let interned_id = db.intern_type_param_id(id);
@@ -314,16 +368,17 @@ impl ToChalk for Ty {
314 ); 368 );
315 Ty::Placeholder(db.lookup_intern_type_param_id(interned_id)) 369 Ty::Placeholder(db.lookup_intern_type_param_id(interned_id))
316 } 370 }
317 chalk_ir::TyData::Alias(proj) => { 371 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => {
318 let associated_ty = from_chalk(db, proj.associated_ty_id); 372 let associated_ty = from_chalk(db, proj.associated_ty_id);
319 let parameters = from_chalk(db, proj.substitution); 373 let parameters = from_chalk(db, proj.substitution);
320 Ty::Projection(ProjectionTy { associated_ty, parameters }) 374 Ty::Projection(ProjectionTy { associated_ty, parameters })
321 } 375 }
376 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(),
322 chalk_ir::TyData::Function(_) => unimplemented!(), 377 chalk_ir::TyData::Function(_) => unimplemented!(),
323 chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx), 378 chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx),
324 chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown, 379 chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown,
325 chalk_ir::TyData::Dyn(where_clauses) => { 380 chalk_ir::TyData::Dyn(where_clauses) => {
326 assert_eq!(where_clauses.bounds.binders.len(), 1); 381 assert_eq!(where_clauses.bounds.binders.len(&Interner), 1);
327 let predicates = where_clauses 382 let predicates = where_clauses
328 .bounds 383 .bounds
329 .skip_binders() 384 .skip_binders()
@@ -404,6 +459,7 @@ impl ToChalk for TypeCtor {
404 match type_name { 459 match type_name {
405 TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), 460 TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()),
406 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), 461 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
462 TypeName::OpaqueType(_) => unreachable!(),
407 TypeName::Error => { 463 TypeName::Error => {
408 // this should not be reached, since we don't represent TypeName::Error with TypeCtor 464 // this should not be reached, since we don't represent TypeName::Error with TypeCtor
409 unreachable!() 465 unreachable!()
@@ -460,7 +516,8 @@ impl ToChalk for GenericPredicate {
460 } 516 }
461 GenericPredicate::Projection(projection_pred) => { 517 GenericPredicate::Projection(projection_pred) => {
462 let ty = projection_pred.ty.to_chalk(db).shifted_in(&Interner); 518 let ty = projection_pred.ty.to_chalk(db).shifted_in(&Interner);
463 let alias = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner); 519 let projection = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner);
520 let alias = chalk_ir::AliasTy::Projection(projection);
464 make_binders(chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), 0) 521 make_binders(chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), 0)
465 } 522 }
466 GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"), 523 GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"),
@@ -481,7 +538,13 @@ impl ToChalk for GenericPredicate {
481 GenericPredicate::Implemented(from_chalk(db, tr)) 538 GenericPredicate::Implemented(from_chalk(db, tr))
482 } 539 }
483 chalk_ir::WhereClause::AliasEq(projection_eq) => { 540 chalk_ir::WhereClause::AliasEq(projection_eq) => {
484 let projection_ty = from_chalk(db, projection_eq.alias); 541 let projection_ty = from_chalk(
542 db,
543 match projection_eq.alias {
544 chalk_ir::AliasTy::Projection(p) => p,
545 _ => unimplemented!(),
546 },
547 );
485 let ty = from_chalk(db, projection_eq.ty); 548 let ty = from_chalk(db, projection_eq.ty);
486 GenericPredicate::Projection(super::ProjectionPredicate { projection_ty, ty }) 549 GenericPredicate::Projection(super::ProjectionPredicate { projection_ty, ty })
487 } 550 }
@@ -490,10 +553,10 @@ impl ToChalk for GenericPredicate {
490} 553}
491 554
492impl ToChalk for ProjectionTy { 555impl ToChalk for ProjectionTy {
493 type Chalk = chalk_ir::AliasTy<Interner>; 556 type Chalk = chalk_ir::ProjectionTy<Interner>;
494 557
495 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasTy<Interner> { 558 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy<Interner> {
496 chalk_ir::AliasTy { 559 chalk_ir::ProjectionTy {
497 associated_ty_id: self.associated_ty.to_chalk(db), 560 associated_ty_id: self.associated_ty.to_chalk(db),
498 substitution: self.parameters.to_chalk(db), 561 substitution: self.parameters.to_chalk(db),
499 } 562 }
@@ -501,7 +564,7 @@ impl ToChalk for ProjectionTy {
501 564
502 fn from_chalk( 565 fn from_chalk(
503 db: &dyn HirDatabase, 566 db: &dyn HirDatabase,
504 projection_ty: chalk_ir::AliasTy<Interner>, 567 projection_ty: chalk_ir::ProjectionTy<Interner>,
505 ) -> ProjectionTy { 568 ) -> ProjectionTy {
506 ProjectionTy { 569 ProjectionTy {
507 associated_ty: from_chalk(db, projection_ty.associated_ty_id), 570 associated_ty: from_chalk(db, projection_ty.associated_ty_id),
@@ -514,7 +577,10 @@ impl ToChalk for super::ProjectionPredicate {
514 type Chalk = chalk_ir::AliasEq<Interner>; 577 type Chalk = chalk_ir::AliasEq<Interner>;
515 578
516 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> { 579 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> {
517 chalk_ir::AliasEq { alias: self.projection_ty.to_chalk(db), ty: self.ty.to_chalk(db) } 580 chalk_ir::AliasEq {
581 alias: chalk_ir::AliasTy::Projection(self.projection_ty.to_chalk(db)),
582 ty: self.ty.to_chalk(db),
583 }
518 } 584 }
519 585
520 fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq<Interner>) -> Self { 586 fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq<Interner>) -> Self {
@@ -540,17 +606,24 @@ impl ToChalk for Obligation {
540impl<T> ToChalk for Canonical<T> 606impl<T> ToChalk for Canonical<T>
541where 607where
542 T: ToChalk, 608 T: ToChalk,
609 T::Chalk: HasInterner<Interner = Interner>,
543{ 610{
544 type Chalk = chalk_ir::Canonical<T::Chalk>; 611 type Chalk = chalk_ir::Canonical<T::Chalk>;
545 612
546 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> { 613 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> {
547 let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT); 614 let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT);
548 let value = self.value.to_chalk(db); 615 let value = self.value.to_chalk(db);
549 chalk_ir::Canonical { value, binders: vec![parameter; self.num_vars] } 616 chalk_ir::Canonical {
617 value,
618 binders: chalk_ir::CanonicalVarKinds::from(&Interner, vec![parameter; self.num_vars]),
619 }
550 } 620 }
551 621
552 fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> { 622 fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> {
553 Canonical { num_vars: canonical.binders.len(), value: from_chalk(db, canonical.value) } 623 Canonical {
624 num_vars: canonical.binders.len(&Interner),
625 value: from_chalk(db, canonical.value),
626 }
554 } 627 }
555} 628}
556 629
@@ -649,9 +722,15 @@ impl ToChalk for builtin::BuiltinImplAssocTyValueData {
649 } 722 }
650} 723}
651 724
652fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> { 725fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T>
726where
727 T: HasInterner<Interner = Interner>,
728{
653 chalk_ir::Binders::new( 729 chalk_ir::Binders::new(
654 std::iter::repeat(chalk_ir::ParameterKind::Ty(())).take(num_vars).collect(), 730 chalk_ir::ParameterKinds::from(
731 &Interner,
732 std::iter::repeat(chalk_ir::ParameterKind::Ty(())).take(num_vars),
733 ),
655 value, 734 value,
656 ) 735 )
657} 736}
@@ -799,6 +878,28 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
799 // FIXME tell Chalk about well-known traits (here and in trait_datum) 878 // FIXME tell Chalk about well-known traits (here and in trait_datum)
800 None 879 None
801 } 880 }
881
882 fn program_clauses_for_env(
883 &self,
884 environment: &chalk_ir::Environment<Interner>,
885 ) -> chalk_ir::ProgramClauses<Interner> {
886 self.db.program_clauses_for_chalk_env(self.krate, environment.clone())
887 }
888
889 fn opaque_ty_data(
890 &self,
891 _id: chalk_ir::OpaqueTyId<Interner>,
892 ) -> Arc<chalk_rust_ir::OpaqueTyDatum<Interner>> {
893 unimplemented!()
894 }
895}
896
897pub(crate) fn program_clauses_for_chalk_env_query(
898 db: &dyn HirDatabase,
899 krate: CrateId,
900 environment: chalk_ir::Environment<Interner>,
901) -> chalk_ir::ProgramClauses<Interner> {
902 chalk_solve::program_clauses_for_env(&ChalkContext { db, krate }, &environment)
802} 903}
803 904
804pub(crate) fn associated_ty_data_query( 905pub(crate) fn associated_ty_data_query(
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs
index fa8e4d1ad..4867cb17e 100644
--- a/crates/ra_hir_ty/src/traits/chalk/tls.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs
@@ -121,19 +121,38 @@ impl DebugContext<'_> {
121 write!(fmt, "{}::{}", trait_data.name, type_alias_data.name) 121 write!(fmt, "{}::{}", trait_data.name, type_alias_data.name)
122 } 122 }
123 123
124 pub fn debug_opaque_ty_id(
125 &self,
126 opaque_ty_id: chalk_ir::OpaqueTyId<Interner>,
127 fmt: &mut fmt::Formatter<'_>,
128 ) -> Result<(), fmt::Error> {
129 fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish()
130 }
131
124 pub fn debug_alias( 132 pub fn debug_alias(
125 &self, 133 &self,
126 alias: &AliasTy<Interner>, 134 alias_ty: &AliasTy<Interner>,
135 fmt: &mut fmt::Formatter<'_>,
136 ) -> Result<(), fmt::Error> {
137 match alias_ty {
138 AliasTy::Projection(projection_ty) => self.debug_projection_ty(projection_ty, fmt),
139 AliasTy::Opaque(opaque_ty) => self.debug_opaque_ty(opaque_ty, fmt),
140 }
141 }
142
143 pub fn debug_projection_ty(
144 &self,
145 projection_ty: &chalk_ir::ProjectionTy<Interner>,
127 fmt: &mut fmt::Formatter<'_>, 146 fmt: &mut fmt::Formatter<'_>,
128 ) -> Result<(), fmt::Error> { 147 ) -> Result<(), fmt::Error> {
129 let type_alias: TypeAliasId = from_chalk(self.0, alias.associated_ty_id); 148 let type_alias: TypeAliasId = from_chalk(self.0, projection_ty.associated_ty_id);
130 let type_alias_data = self.0.type_alias_data(type_alias); 149 let type_alias_data = self.0.type_alias_data(type_alias);
131 let trait_ = match type_alias.lookup(self.0.upcast()).container { 150 let trait_ = match type_alias.lookup(self.0.upcast()).container {
132 AssocContainerId::TraitId(t) => t, 151 AssocContainerId::TraitId(t) => t,
133 _ => panic!("associated type not in trait"), 152 _ => panic!("associated type not in trait"),
134 }; 153 };
135 let trait_data = self.0.trait_data(trait_); 154 let trait_data = self.0.trait_data(trait_);
136 let params = alias.substitution.parameters(&Interner); 155 let params = projection_ty.substitution.parameters(&Interner);
137 write!(fmt, "<{:?} as {}", &params[0], trait_data.name,)?; 156 write!(fmt, "<{:?} as {}", &params[0], trait_data.name,)?;
138 if params.len() > 1 { 157 if params.len() > 1 {
139 write!( 158 write!(
@@ -145,6 +164,14 @@ impl DebugContext<'_> {
145 write!(fmt, ">::{}", type_alias_data.name) 164 write!(fmt, ">::{}", type_alias_data.name)
146 } 165 }
147 166
167 pub fn debug_opaque_ty(
168 &self,
169 opaque_ty: &chalk_ir::OpaqueTy<Interner>,
170 fmt: &mut fmt::Formatter<'_>,
171 ) -> Result<(), fmt::Error> {
172 write!(fmt, "{:?}", opaque_ty.opaque_ty_id)
173 }
174
148 pub fn debug_ty( 175 pub fn debug_ty(
149 &self, 176 &self,
150 ty: &chalk_ir::Ty<Interner>, 177 ty: &chalk_ir::Ty<Interner>,
diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs
index 40d56a4f7..2b5d11681 100644
--- a/crates/ra_ide/src/assists.rs
+++ b/crates/ra_ide/src/assists.rs
@@ -37,6 +37,10 @@ fn action_to_edit(
37 file_id: FileId, 37 file_id: FileId,
38 assist_label: &AssistLabel, 38 assist_label: &AssistLabel,
39) -> SourceChange { 39) -> SourceChange {
40 let file_id = match action.file {
41 ra_assists::AssistFile::TargetFile(it) => it,
42 _ => file_id,
43 };
40 let file_edit = SourceFileEdit { file_id, edit: action.edit }; 44 let file_edit = SourceFileEdit { file_id, edit: action.edit };
41 SourceChange::source_file_edit(assist_label.label.clone(), file_edit) 45 SourceChange::source_file_edit(assist_label.label.clone(), file_edit)
42 .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id })) 46 .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id }))
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index f95b6baf3..5da254a6e 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -19,10 +19,24 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
19 call_info_for_token(&sema, token) 19 call_info_for_token(&sema, token)
20} 20}
21 21
22pub(crate) fn call_info_for_token( 22#[derive(Debug)]
23 sema: &Semantics<RootDatabase>, 23pub(crate) struct ActiveParameter {
24 token: SyntaxToken, 24 /// FIXME: should be `Type` and `Name
25) -> Option<CallInfo> { 25 pub(crate) ty: String,
26 pub(crate) name: String,
27}
28
29impl ActiveParameter {
30 pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> {
31 call_info(db, position)?.into_active_parameter()
32 }
33
34 pub(crate) fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
35 call_info_for_token(sema, token)?.into_active_parameter()
36 }
37}
38
39fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<CallInfo> {
26 // Find the calling expression and it's NameRef 40 // Find the calling expression and it's NameRef
27 let calling_node = FnCallNode::with_node(&token.parent())?; 41 let calling_node = FnCallNode::with_node(&token.parent())?;
28 42
@@ -160,6 +174,14 @@ impl FnCallNode {
160} 174}
161 175
162impl CallInfo { 176impl CallInfo {
177 fn into_active_parameter(self) -> Option<ActiveParameter> {
178 let idx = self.active_parameter?;
179 let ty = self.signature.parameter_types.get(idx)?.clone();
180 let name = self.signature.parameter_names.get(idx)?.clone();
181 let res = ActiveParameter { ty, name };
182 Some(res)
183 }
184
163 fn with_fn(db: &RootDatabase, function: hir::Function) -> Self { 185 fn with_fn(db: &RootDatabase, function: hir::Function) -> Self {
164 let signature = FunctionSignature::from_hir(db, function); 186 let signature = FunctionSignature::from_hir(db, function);
165 187
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index 4a1a2a04a..4ca0fdf4f 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -1,9 +1,11 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3mod completion_config;
3mod completion_item; 4mod completion_item;
4mod completion_context; 5mod completion_context;
5mod presentation; 6mod presentation;
6 7
8mod complete_attribute;
7mod complete_dot; 9mod complete_dot;
8mod complete_record; 10mod complete_record;
9mod complete_pattern; 11mod complete_pattern;
@@ -28,27 +30,11 @@ use crate::{
28 FilePosition, 30 FilePosition,
29}; 31};
30 32
31pub use crate::completion::completion_item::{ 33pub use crate::completion::{
32 CompletionItem, CompletionItemKind, InsertTextFormat, 34 completion_config::CompletionConfig,
35 completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
33}; 36};
34 37
35#[derive(Clone, Debug, PartialEq, Eq)]
36pub struct CompletionConfig {
37 pub enable_postfix_completions: bool,
38 pub add_call_parenthesis: bool,
39 pub add_call_argument_snippets: bool,
40}
41
42impl Default for CompletionConfig {
43 fn default() -> Self {
44 CompletionConfig {
45 enable_postfix_completions: true,
46 add_call_parenthesis: true,
47 add_call_argument_snippets: true,
48 }
49 }
50}
51
52/// Main entry point for completion. We run completion as a two-phase process. 38/// Main entry point for completion. We run completion as a two-phase process.
53/// 39///
54/// First, we look at the position and collect a so-called `CompletionContext. 40/// First, we look at the position and collect a so-called `CompletionContext.
@@ -93,6 +79,7 @@ pub(crate) fn completions(
93 complete_postfix::complete_postfix(&mut acc, &ctx); 79 complete_postfix::complete_postfix(&mut acc, &ctx);
94 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); 80 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
95 complete_trait_impl::complete_trait_impl(&mut acc, &ctx); 81 complete_trait_impl::complete_trait_impl(&mut acc, &ctx);
82 complete_attribute::complete_attribute(&mut acc, &ctx);
96 83
97 Some(acc) 84 Some(acc)
98} 85}
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
new file mode 100644
index 000000000..b405042e8
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -0,0 +1,587 @@
1//! Completion for attributes
2//!
3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes.
5
6use super::completion_context::CompletionContext;
7use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions};
8use ra_syntax::{
9 ast::{Attr, AttrKind},
10 AstNode,
11};
12
13pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) {
14 if !ctx.is_attribute {
15 return;
16 }
17
18 let is_inner = ctx
19 .original_token
20 .ancestors()
21 .find_map(Attr::cast)
22 .map(|attr| attr.kind() == AttrKind::Inner)
23 .unwrap_or(false);
24
25 for attr_completion in ATTRIBUTES {
26 let mut item = CompletionItem::new(
27 CompletionKind::Attribute,
28 ctx.source_range(),
29 attr_completion.label,
30 )
31 .kind(CompletionItemKind::Attribute);
32
33 match (attr_completion.snippet, ctx.config.snippet_cap) {
34 (Some(snippet), Some(cap)) => {
35 item = item.insert_snippet(cap, snippet);
36 }
37 _ => {}
38 }
39
40 if is_inner || !attr_completion.should_be_inner {
41 acc.add(item);
42 }
43 }
44}
45
46struct AttrCompletion {
47 label: &'static str,
48 snippet: Option<&'static str>,
49 should_be_inner: bool,
50}
51
52const ATTRIBUTES: &[AttrCompletion] = &[
53 AttrCompletion { label: "allow", snippet: Some("allow(${0:lint})"), should_be_inner: false },
54 AttrCompletion {
55 label: "cfg_attr",
56 snippet: Some("cfg_attr(${1:predicate}, ${0:attr})"),
57 should_be_inner: false,
58 },
59 AttrCompletion { label: "cfg", snippet: Some("cfg(${0:predicate})"), should_be_inner: false },
60 AttrCompletion { label: "deny", snippet: Some("deny(${0:lint})"), should_be_inner: false },
61 AttrCompletion {
62 label: "deprecated",
63 snippet: Some(r#"deprecated = "${0:reason}""#),
64 should_be_inner: false,
65 },
66 AttrCompletion {
67 label: "derive",
68 snippet: Some(r#"derive(${0:Debug})"#),
69 should_be_inner: false,
70 },
71 AttrCompletion { label: "doc", snippet: Some(r#"doc = "${0:docs}""#), should_be_inner: false },
72 AttrCompletion { label: "feature", snippet: Some("feature(${0:flag})"), should_be_inner: true },
73 AttrCompletion { label: "forbid", snippet: Some("forbid(${0:lint})"), should_be_inner: false },
74 // FIXME: resolve through macro resolution?
75 AttrCompletion { label: "global_allocator", snippet: None, should_be_inner: true },
76 AttrCompletion { label: "ignore", snippet: Some("ignore(${0:lint})"), should_be_inner: false },
77 AttrCompletion { label: "inline", snippet: Some("inline(${0:lint})"), should_be_inner: false },
78 AttrCompletion {
79 label: "link_name",
80 snippet: Some(r#"link_name = "${0:symbol_name}""#),
81 should_be_inner: false,
82 },
83 AttrCompletion { label: "link", snippet: None, should_be_inner: false },
84 AttrCompletion { label: "macro_export", snippet: None, should_be_inner: false },
85 AttrCompletion { label: "macro_use", snippet: None, should_be_inner: false },
86 AttrCompletion {
87 label: "must_use",
88 snippet: Some(r#"must_use = "${0:reason}""#),
89 should_be_inner: false,
90 },
91 AttrCompletion { label: "no_mangle", snippet: None, should_be_inner: false },
92 AttrCompletion { label: "no_std", snippet: None, should_be_inner: true },
93 AttrCompletion { label: "non_exhaustive", snippet: None, should_be_inner: false },
94 AttrCompletion { label: "panic_handler", snippet: None, should_be_inner: true },
95 AttrCompletion { label: "path", snippet: Some("path =\"${0:path}\""), should_be_inner: false },
96 AttrCompletion { label: "proc_macro", snippet: None, should_be_inner: false },
97 AttrCompletion { label: "proc_macro_attribute", snippet: None, should_be_inner: false },
98 AttrCompletion {
99 label: "proc_macro_derive",
100 snippet: Some("proc_macro_derive(${0:Trait})"),
101 should_be_inner: false,
102 },
103 AttrCompletion {
104 label: "recursion_limit",
105 snippet: Some("recursion_limit = ${0:128}"),
106 should_be_inner: true,
107 },
108 AttrCompletion { label: "repr", snippet: Some("repr(${0:C})"), should_be_inner: false },
109 AttrCompletion {
110 label: "should_panic",
111 snippet: Some(r#"expected = "${0:reason}""#),
112 should_be_inner: false,
113 },
114 AttrCompletion {
115 label: "target_feature",
116 snippet: Some("target_feature = \"${0:feature}\""),
117 should_be_inner: false,
118 },
119 AttrCompletion { label: "test", snippet: None, should_be_inner: false },
120 AttrCompletion { label: "used", snippet: None, should_be_inner: false },
121 AttrCompletion { label: "warn", snippet: Some("warn(${0:lint})"), should_be_inner: false },
122 AttrCompletion {
123 label: "windows_subsystem",
124 snippet: Some(r#"windows_subsystem = "${0:subsystem}""#),
125 should_be_inner: true,
126 },
127];
128
129#[cfg(test)]
130mod tests {
131 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
132 use insta::assert_debug_snapshot;
133
134 fn do_attr_completion(code: &str) -> Vec<CompletionItem> {
135 do_completion(code, CompletionKind::Attribute)
136 }
137
138 #[test]
139 fn test_attribute_completion() {
140 assert_debug_snapshot!(
141 do_attr_completion(
142 r"
143 #[<|>]
144 ",
145 ),
146 @r###"
147 [
148 CompletionItem {
149 label: "allow",
150 source_range: [19; 19),
151 delete: [19; 19),
152 insert: "allow(${0:lint})",
153 kind: Attribute,
154 },
155 CompletionItem {
156 label: "cfg",
157 source_range: [19; 19),
158 delete: [19; 19),
159 insert: "cfg(${0:predicate})",
160 kind: Attribute,
161 },
162 CompletionItem {
163 label: "cfg_attr",
164 source_range: [19; 19),
165 delete: [19; 19),
166 insert: "cfg_attr(${1:predicate}, ${0:attr})",
167 kind: Attribute,
168 },
169 CompletionItem {
170 label: "deny",
171 source_range: [19; 19),
172 delete: [19; 19),
173 insert: "deny(${0:lint})",
174 kind: Attribute,
175 },
176 CompletionItem {
177 label: "deprecated",
178 source_range: [19; 19),
179 delete: [19; 19),
180 insert: "deprecated = \"${0:reason}\"",
181 kind: Attribute,
182 },
183 CompletionItem {
184 label: "derive",
185 source_range: [19; 19),
186 delete: [19; 19),
187 insert: "derive(${0:Debug})",
188 kind: Attribute,
189 },
190 CompletionItem {
191 label: "doc",
192 source_range: [19; 19),
193 delete: [19; 19),
194 insert: "doc = \"${0:docs}\"",
195 kind: Attribute,
196 },
197 CompletionItem {
198 label: "forbid",
199 source_range: [19; 19),
200 delete: [19; 19),
201 insert: "forbid(${0:lint})",
202 kind: Attribute,
203 },
204 CompletionItem {
205 label: "ignore",
206 source_range: [19; 19),
207 delete: [19; 19),
208 insert: "ignore(${0:lint})",
209 kind: Attribute,
210 },
211 CompletionItem {
212 label: "inline",
213 source_range: [19; 19),
214 delete: [19; 19),
215 insert: "inline(${0:lint})",
216 kind: Attribute,
217 },
218 CompletionItem {
219 label: "link",
220 source_range: [19; 19),
221 delete: [19; 19),
222 insert: "link",
223 kind: Attribute,
224 },
225 CompletionItem {
226 label: "link_name",
227 source_range: [19; 19),
228 delete: [19; 19),
229 insert: "link_name = \"${0:symbol_name}\"",
230 kind: Attribute,
231 },
232 CompletionItem {
233 label: "macro_export",
234 source_range: [19; 19),
235 delete: [19; 19),
236 insert: "macro_export",
237 kind: Attribute,
238 },
239 CompletionItem {
240 label: "macro_use",
241 source_range: [19; 19),
242 delete: [19; 19),
243 insert: "macro_use",
244 kind: Attribute,
245 },
246 CompletionItem {
247 label: "must_use",
248 source_range: [19; 19),
249 delete: [19; 19),
250 insert: "must_use = \"${0:reason}\"",
251 kind: Attribute,
252 },
253 CompletionItem {
254 label: "no_mangle",
255 source_range: [19; 19),
256 delete: [19; 19),
257 insert: "no_mangle",
258 kind: Attribute,
259 },
260 CompletionItem {
261 label: "non_exhaustive",
262 source_range: [19; 19),
263 delete: [19; 19),
264 insert: "non_exhaustive",
265 kind: Attribute,
266 },
267 CompletionItem {
268 label: "path",
269 source_range: [19; 19),
270 delete: [19; 19),
271 insert: "path =\"${0:path}\"",
272 kind: Attribute,
273 },
274 CompletionItem {
275 label: "proc_macro",
276 source_range: [19; 19),
277 delete: [19; 19),
278 insert: "proc_macro",
279 kind: Attribute,
280 },
281 CompletionItem {
282 label: "proc_macro_attribute",
283 source_range: [19; 19),
284 delete: [19; 19),
285 insert: "proc_macro_attribute",
286 kind: Attribute,
287 },
288 CompletionItem {
289 label: "proc_macro_derive",
290 source_range: [19; 19),
291 delete: [19; 19),
292 insert: "proc_macro_derive(${0:Trait})",
293 kind: Attribute,
294 },
295 CompletionItem {
296 label: "repr",
297 source_range: [19; 19),
298 delete: [19; 19),
299 insert: "repr(${0:C})",
300 kind: Attribute,
301 },
302 CompletionItem {
303 label: "should_panic",
304 source_range: [19; 19),
305 delete: [19; 19),
306 insert: "expected = \"${0:reason}\"",
307 kind: Attribute,
308 },
309 CompletionItem {
310 label: "target_feature",
311 source_range: [19; 19),
312 delete: [19; 19),
313 insert: "target_feature = \"${0:feature}\"",
314 kind: Attribute,
315 },
316 CompletionItem {
317 label: "test",
318 source_range: [19; 19),
319 delete: [19; 19),
320 insert: "test",
321 kind: Attribute,
322 },
323 CompletionItem {
324 label: "used",
325 source_range: [19; 19),
326 delete: [19; 19),
327 insert: "used",
328 kind: Attribute,
329 },
330 CompletionItem {
331 label: "warn",
332 source_range: [19; 19),
333 delete: [19; 19),
334 insert: "warn(${0:lint})",
335 kind: Attribute,
336 },
337 ]
338 "###
339 );
340 }
341
342 #[test]
343 fn test_inner_attribute_completion() {
344 assert_debug_snapshot!(
345 do_attr_completion(
346 r"
347 #![<|>]
348 ",
349 ),
350 @r###"
351 [
352 CompletionItem {
353 label: "allow",
354 source_range: [20; 20),
355 delete: [20; 20),
356 insert: "allow(${0:lint})",
357 kind: Attribute,
358 },
359 CompletionItem {
360 label: "cfg",
361 source_range: [20; 20),
362 delete: [20; 20),
363 insert: "cfg(${0:predicate})",
364 kind: Attribute,
365 },
366 CompletionItem {
367 label: "cfg_attr",
368 source_range: [20; 20),
369 delete: [20; 20),
370 insert: "cfg_attr(${1:predicate}, ${0:attr})",
371 kind: Attribute,
372 },
373 CompletionItem {
374 label: "deny",
375 source_range: [20; 20),
376 delete: [20; 20),
377 insert: "deny(${0:lint})",
378 kind: Attribute,
379 },
380 CompletionItem {
381 label: "deprecated",
382 source_range: [20; 20),
383 delete: [20; 20),
384 insert: "deprecated = \"${0:reason}\"",
385 kind: Attribute,
386 },
387 CompletionItem {
388 label: "derive",
389 source_range: [20; 20),
390 delete: [20; 20),
391 insert: "derive(${0:Debug})",
392 kind: Attribute,
393 },
394 CompletionItem {
395 label: "doc",
396 source_range: [20; 20),
397 delete: [20; 20),
398 insert: "doc = \"${0:docs}\"",
399 kind: Attribute,
400 },
401 CompletionItem {
402 label: "feature",
403 source_range: [20; 20),
404 delete: [20; 20),
405 insert: "feature(${0:flag})",
406 kind: Attribute,
407 },
408 CompletionItem {
409 label: "forbid",
410 source_range: [20; 20),
411 delete: [20; 20),
412 insert: "forbid(${0:lint})",
413 kind: Attribute,
414 },
415 CompletionItem {
416 label: "global_allocator",
417 source_range: [20; 20),
418 delete: [20; 20),
419 insert: "global_allocator",
420 kind: Attribute,
421 },
422 CompletionItem {
423 label: "ignore",
424 source_range: [20; 20),
425 delete: [20; 20),
426 insert: "ignore(${0:lint})",
427 kind: Attribute,
428 },
429 CompletionItem {
430 label: "inline",
431 source_range: [20; 20),
432 delete: [20; 20),
433 insert: "inline(${0:lint})",
434 kind: Attribute,
435 },
436 CompletionItem {
437 label: "link",
438 source_range: [20; 20),
439 delete: [20; 20),
440 insert: "link",
441 kind: Attribute,
442 },
443 CompletionItem {
444 label: "link_name",
445 source_range: [20; 20),
446 delete: [20; 20),
447 insert: "link_name = \"${0:symbol_name}\"",
448 kind: Attribute,
449 },
450 CompletionItem {
451 label: "macro_export",
452 source_range: [20; 20),
453 delete: [20; 20),
454 insert: "macro_export",
455 kind: Attribute,
456 },
457 CompletionItem {
458 label: "macro_use",
459 source_range: [20; 20),
460 delete: [20; 20),
461 insert: "macro_use",
462 kind: Attribute,
463 },
464 CompletionItem {
465 label: "must_use",
466 source_range: [20; 20),
467 delete: [20; 20),
468 insert: "must_use = \"${0:reason}\"",
469 kind: Attribute,
470 },
471 CompletionItem {
472 label: "no_mangle",
473 source_range: [20; 20),
474 delete: [20; 20),
475 insert: "no_mangle",
476 kind: Attribute,
477 },
478 CompletionItem {
479 label: "no_std",
480 source_range: [20; 20),
481 delete: [20; 20),
482 insert: "no_std",
483 kind: Attribute,
484 },
485 CompletionItem {
486 label: "non_exhaustive",
487 source_range: [20; 20),
488 delete: [20; 20),
489 insert: "non_exhaustive",
490 kind: Attribute,
491 },
492 CompletionItem {
493 label: "panic_handler",
494 source_range: [20; 20),
495 delete: [20; 20),
496 insert: "panic_handler",
497 kind: Attribute,
498 },
499 CompletionItem {
500 label: "path",
501 source_range: [20; 20),
502 delete: [20; 20),
503 insert: "path =\"${0:path}\"",
504 kind: Attribute,
505 },
506 CompletionItem {
507 label: "proc_macro",
508 source_range: [20; 20),
509 delete: [20; 20),
510 insert: "proc_macro",
511 kind: Attribute,
512 },
513 CompletionItem {
514 label: "proc_macro_attribute",
515 source_range: [20; 20),
516 delete: [20; 20),
517 insert: "proc_macro_attribute",
518 kind: Attribute,
519 },
520 CompletionItem {
521 label: "proc_macro_derive",
522 source_range: [20; 20),
523 delete: [20; 20),
524 insert: "proc_macro_derive(${0:Trait})",
525 kind: Attribute,
526 },
527 CompletionItem {
528 label: "recursion_limit",
529 source_range: [20; 20),
530 delete: [20; 20),
531 insert: "recursion_limit = ${0:128}",
532 kind: Attribute,
533 },
534 CompletionItem {
535 label: "repr",
536 source_range: [20; 20),
537 delete: [20; 20),
538 insert: "repr(${0:C})",
539 kind: Attribute,
540 },
541 CompletionItem {
542 label: "should_panic",
543 source_range: [20; 20),
544 delete: [20; 20),
545 insert: "expected = \"${0:reason}\"",
546 kind: Attribute,
547 },
548 CompletionItem {
549 label: "target_feature",
550 source_range: [20; 20),
551 delete: [20; 20),
552 insert: "target_feature = \"${0:feature}\"",
553 kind: Attribute,
554 },
555 CompletionItem {
556 label: "test",
557 source_range: [20; 20),
558 delete: [20; 20),
559 insert: "test",
560 kind: Attribute,
561 },
562 CompletionItem {
563 label: "used",
564 source_range: [20; 20),
565 delete: [20; 20),
566 insert: "used",
567 kind: Attribute,
568 },
569 CompletionItem {
570 label: "warn",
571 source_range: [20; 20),
572 delete: [20; 20),
573 insert: "warn(${0:lint})",
574 kind: Attribute,
575 },
576 CompletionItem {
577 label: "windows_subsystem",
578 source_range: [20; 20),
579 delete: [20; 20),
580 insert: "windows_subsystem = \"${0:subsystem}\"",
581 kind: Attribute,
582 },
583 ]
584 "###
585 );
586 }
587}
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index f433faef3..b93153b48 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -2,9 +2,11 @@
2 2
3use hir::{HasVisibility, Type}; 3use hir::{HasVisibility, Type};
4 4
5use crate::completion::completion_item::CompletionKind;
6use crate::{ 5use crate::{
7 completion::{completion_context::CompletionContext, completion_item::Completions}, 6 completion::{
7 completion_context::CompletionContext,
8 completion_item::{CompletionKind, Completions},
9 },
8 CompletionItem, 10 CompletionItem,
9}; 11};
10use rustc_hash::FxHashSet; 12use rustc_hash::FxHashSet;
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index 38f9c34e7..adefb290e 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -42,10 +42,14 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
42} 42}
43 43
44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { 44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
45 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) 45 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
46 .kind(CompletionItemKind::Keyword) 46 .kind(CompletionItemKind::Keyword);
47 .insert_snippet(snippet) 47
48 .build() 48 match ctx.config.snippet_cap {
49 Some(cap) => res.insert_snippet(cap, snippet),
50 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
51 }
52 .build()
49} 53}
50 54
51pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 55pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
diff --git a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
index 270e96df0..6000106d0 100644
--- a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
+++ b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
@@ -41,7 +41,7 @@ mod tests {
41 @r###" 41 @r###"
42 [ 42 [
43 CompletionItem { 43 CompletionItem {
44 label: "foo!", 44 label: "foo!(…)",
45 source_range: [46; 46), 45 source_range: [46; 46),
46 delete: [46; 46), 46 delete: [46; 46),
47 insert: "foo!($0)", 47 insert: "foo!($0)",
@@ -81,7 +81,7 @@ mod tests {
81 @r###" 81 @r###"
82 [ 82 [
83 CompletionItem { 83 CompletionItem {
84 label: "vec!", 84 label: "vec![…]",
85 source_range: [280; 280), 85 source_range: [280; 280),
86 delete: [280; 280), 86 delete: [280; 280),
87 insert: "vec![$0]", 87 insert: "vec![$0]",
@@ -118,7 +118,7 @@ mod tests {
118 @r###" 118 @r###"
119 [ 119 [
120 CompletionItem { 120 CompletionItem {
121 label: "foo!", 121 label: "foo! {…}",
122 source_range: [163; 163), 122 source_range: [163; 163),
123 delete: [163; 163), 123 delete: [163; 163),
124 insert: "foo! {$0}", 124 insert: "foo! {$0}",
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs
index a8b4ce114..218829b10 100644
--- a/crates/ra_ide/src/completion/complete_pattern.rs
+++ b/crates/ra_ide/src/completion/complete_pattern.rs
@@ -125,7 +125,7 @@ mod tests {
125 kind: Enum, 125 kind: Enum,
126 }, 126 },
127 CompletionItem { 127 CompletionItem {
128 label: "m!", 128 label: "m!(…)",
129 source_range: [151; 151), 129 source_range: [151; 151),
130 delete: [151; 151), 130 delete: [151; 151),
131 insert: "m!($0)", 131 insert: "m!($0)",
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 29c2881c6..8d397b0fe 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -6,6 +6,7 @@ use ra_syntax::{
6}; 6};
7use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
8 8
9use super::completion_config::SnippetCap;
9use crate::{ 10use crate::{
10 completion::{ 11 completion::{
11 completion_context::CompletionContext, 12 completion_context::CompletionContext,
@@ -32,9 +33,15 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
32 None => return, 33 None => return,
33 }; 34 };
34 35
36 let cap = match ctx.config.snippet_cap {
37 Some(it) => it,
38 None => return,
39 };
40
35 if receiver_ty.is_bool() || receiver_ty.is_unknown() { 41 if receiver_ty.is_bool() || receiver_ty.is_unknown() {
36 postfix_snippet( 42 postfix_snippet(
37 ctx, 43 ctx,
44 cap,
38 &dot_receiver, 45 &dot_receiver,
39 "if", 46 "if",
40 "if expr {}", 47 "if expr {}",
@@ -43,6 +50,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
43 .add_to(acc); 50 .add_to(acc);
44 postfix_snippet( 51 postfix_snippet(
45 ctx, 52 ctx,
53 cap,
46 &dot_receiver, 54 &dot_receiver,
47 "while", 55 "while",
48 "while expr {}", 56 "while expr {}",
@@ -52,11 +60,20 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
52 } 60 }
53 61
54 // !&&&42 is a compiler error, ergo process it before considering the references 62 // !&&&42 is a compiler error, ergo process it before considering the references
55 postfix_snippet(ctx, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc); 63 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
64 .add_to(acc);
56 65
57 postfix_snippet(ctx, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc); 66 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
58 postfix_snippet(ctx, &dot_receiver, "refm", "&mut expr", &format!("&mut {}", receiver_text))
59 .add_to(acc); 67 .add_to(acc);
68 postfix_snippet(
69 ctx,
70 cap,
71 &dot_receiver,
72 "refm",
73 "&mut expr",
74 &format!("&mut {}", receiver_text),
75 )
76 .add_to(acc);
60 77
61 // The rest of the postfix completions create an expression that moves an argument, 78 // The rest of the postfix completions create an expression that moves an argument,
62 // so it's better to consider references now to avoid breaking the compilation 79 // so it's better to consider references now to avoid breaking the compilation
@@ -66,6 +83,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
66 83
67 postfix_snippet( 84 postfix_snippet(
68 ctx, 85 ctx,
86 cap,
69 &dot_receiver, 87 &dot_receiver,
70 "match", 88 "match",
71 "match expr {}", 89 "match expr {}",
@@ -75,6 +93,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
75 93
76 postfix_snippet( 94 postfix_snippet(
77 ctx, 95 ctx,
96 cap,
78 &dot_receiver, 97 &dot_receiver,
79 "box", 98 "box",
80 "Box::new(expr)", 99 "Box::new(expr)",
@@ -82,8 +101,15 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
82 ) 101 )
83 .add_to(acc); 102 .add_to(acc);
84 103
85 postfix_snippet(ctx, &dot_receiver, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)) 104 postfix_snippet(
86 .add_to(acc); 105 ctx,
106 cap,
107 &dot_receiver,
108 "dbg",
109 "dbg!(expr)",
110 &format!("dbg!({})", receiver_text),
111 )
112 .add_to(acc);
87} 113}
88 114
89fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { 115fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
@@ -108,6 +134,7 @@ fn include_references(initial_element: &ast::Expr) -> ast::Expr {
108 134
109fn postfix_snippet( 135fn postfix_snippet(
110 ctx: &CompletionContext, 136 ctx: &CompletionContext,
137 cap: SnippetCap,
111 receiver: &ast::Expr, 138 receiver: &ast::Expr,
112 label: &str, 139 label: &str,
113 detail: &str, 140 detail: &str,
@@ -121,7 +148,7 @@ fn postfix_snippet(
121 }; 148 };
122 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) 149 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
123 .detail(detail) 150 .detail(detail)
124 .snippet_edit(edit) 151 .snippet_edit(cap, edit)
125} 152}
126 153
127#[cfg(test)] 154#[cfg(test)]
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
index d98523406..5a5139e14 100644
--- a/crates/ra_ide/src/completion/complete_qualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_qualified_path.rs
@@ -57,9 +57,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
57 } 57 }
58 match item { 58 match item {
59 hir::AssocItem::Function(func) => { 59 hir::AssocItem::Function(func) => {
60 if !func.has_self_param(ctx.db) { 60 acc.add_function(ctx, func, None);
61 acc.add_function(ctx, func, None);
62 }
63 } 61 }
64 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), 62 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
65 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), 63 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
@@ -86,9 +84,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
86 } 84 }
87 match item { 85 match item {
88 hir::AssocItem::Function(func) => { 86 hir::AssocItem::Function(func) => {
89 if !func.has_self_param(ctx.db) { 87 acc.add_function(ctx, func, None);
90 acc.add_function(ctx, func, None);
91 }
92 } 88 }
93 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), 89 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
94 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), 90 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
@@ -483,6 +479,42 @@ mod tests {
483 } 479 }
484 480
485 #[test] 481 #[test]
482 fn completes_struct_associated_method_with_self() {
483 assert_debug_snapshot!(
484 do_reference_completion(
485 "
486 //- /lib.rs
487 /// A Struct
488 struct S;
489
490 impl S {
491 /// An associated method
492 fn m(&self) { }
493 }
494
495 fn foo() { let _ = S::<|> }
496 "
497 ),
498 @r###"
499 [
500 CompletionItem {
501 label: "m()",
502 source_range: [105; 105),