aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r--crates/ide_completion/src/completions.rs56
-rw-r--r--crates/ide_completion/src/completions/pattern.rs6
-rw-r--r--crates/ide_completion/src/completions/postfix.rs5
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs73
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs69
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs6
-rw-r--r--crates/ide_completion/src/context.rs229
-rw-r--r--crates/ide_completion/src/lib.rs2
-rw-r--r--crates/ide_completion/src/test_utils.rs2
9 files changed, 272 insertions, 176 deletions
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index e2994eed4..78154bf3e 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -203,41 +203,37 @@ impl Completions {
203fn complete_enum_variants( 203fn complete_enum_variants(
204 acc: &mut Completions, 204 acc: &mut Completions,
205 ctx: &CompletionContext, 205 ctx: &CompletionContext,
206 ty: &hir::Type, 206 enum_data: hir::Enum,
207 cb: impl Fn(&mut Completions, &CompletionContext, hir::Variant, hir::ModPath), 207 cb: impl Fn(&mut Completions, &CompletionContext, hir::Variant, hir::ModPath),
208) { 208) {
209 if let Some(hir::Adt::Enum(enum_data)) = 209 let variants = enum_data.variants(ctx.db);
210 iter::successors(Some(ty.clone()), |ty| ty.remove_ref()).last().and_then(|ty| ty.as_adt()) 210
211 { 211 let module = if let Some(module) = ctx.scope.module() {
212 let variants = enum_data.variants(ctx.db); 212 // Compute path from the completion site if available.
213 213 module
214 let module = if let Some(module) = ctx.scope.module() { 214 } else {
215 // Compute path from the completion site if available. 215 // Otherwise fall back to the enum's definition site.
216 module 216 enum_data.module(ctx.db)
217 } else { 217 };
218 // Otherwise fall back to the enum's definition site. 218
219 enum_data.module(ctx.db) 219 if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) {
220 }; 220 if impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_data)) {
221 221 for &variant in &variants {
222 if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { 222 let self_path = hir::ModPath::from_segments(
223 if impl_.self_ty(ctx.db) == *ty { 223 hir::PathKind::Plain,
224 for &variant in &variants { 224 iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))),
225 let self_path = hir::ModPath::from_segments( 225 );
226 hir::PathKind::Plain, 226 cb(acc, ctx, variant, self_path);
227 iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))),
228 );
229 cb(acc, ctx, variant, self_path);
230 }
231 } 227 }
232 } 228 }
229 }
233 230
234 for variant in variants { 231 for variant in variants {
235 if let Some(path) = module.find_use_path(ctx.db, hir::ModuleDef::from(variant)) { 232 if let Some(path) = module.find_use_path(ctx.db, hir::ModuleDef::from(variant)) {
236 // Variants with trivial paths are already added by the existing completion logic, 233 // Variants with trivial paths are already added by the existing completion logic,
237 // so we should avoid adding these twice 234 // so we should avoid adding these twice
238 if path.segments().len() > 1 { 235 if path.segments().len() > 1 {
239 cb(acc, ctx, variant, path); 236 cb(acc, ctx, variant, path);
240 }
241 } 237 }
242 } 238 }
243 } 239 }
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 808d7ff7e..8dc9ab73c 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -12,8 +12,10 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
12 } 12 }
13 13
14 if !ctx.is_irrefutable_pat_binding { 14 if !ctx.is_irrefutable_pat_binding {
15 if let Some(ty) = ctx.expected_type.as_ref() { 15 if let Some(hir::Adt::Enum(e)) =
16 super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| { 16 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
17 {
18 super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| {
17 acc.add_qualified_variant_pat(ctx, variant, path.clone()); 19 acc.add_qualified_variant_pat(ctx, variant, path.clone());
18 acc.add_qualified_enum_variant(ctx, variant, path); 20 acc.add_qualified_enum_variant(ctx, variant, path);
19 }); 21 });
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index ac69b720a..962aaf0df 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -35,14 +35,11 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
35 None => return, 35 None => return,
36 }; 36 };
37 37
38 let ref_removed_ty =
39 std::iter::successors(Some(receiver_ty.clone()), |ty| ty.remove_ref()).last().unwrap();
40
41 let cap = match ctx.config.snippet_cap { 38 let cap = match ctx.config.snippet_cap {
42 Some(it) => it, 39 Some(it) => it,
43 None => return, 40 None => return,
44 }; 41 };
45 let try_enum = TryEnum::from_ty(&ctx.sema, &ref_removed_ty); 42 let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty.strip_references());
46 if let Some(try_enum) = &try_enum { 43 if let Some(try_enum) = &try_enum {
47 match try_enum { 44 match try_enum {
48 TryEnum::Result => { 45 TryEnum::Result => {
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 969249df6..eedb44873 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -52,18 +52,24 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
52 | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) 52 | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_))
53 | PathResolution::Def(def @ hir::ModuleDef::BuiltinType(_)) => { 53 | PathResolution::Def(def @ hir::ModuleDef::BuiltinType(_)) => {
54 if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { 54 if let hir::ModuleDef::Adt(Adt::Enum(e)) = def {
55 for variant in e.variants(ctx.db) { 55 add_enum_variants(ctx, acc, e);
56 acc.add_enum_variant(ctx, variant, None);
57 }
58 } 56 }
59 let ty = match def { 57 let ty = match def {
60 hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), 58 hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
61 hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), 59 hir::ModuleDef::TypeAlias(a) => {
60 let ty = a.ty(ctx.db);
61 if let Some(Adt::Enum(e)) = ty.as_adt() {
62 cov_mark::hit!(completes_variant_through_alias);
63 add_enum_variants(ctx, acc, e);
64 }
65 ty
66 }
62 hir::ModuleDef::BuiltinType(builtin) => { 67 hir::ModuleDef::BuiltinType(builtin) => {
63 let module = match ctx.scope.module() { 68 let module = match ctx.scope.module() {
64 Some(it) => it, 69 Some(it) => it,
65 None => return, 70 None => return,
66 }; 71 };
72 cov_mark::hit!(completes_primitive_assoc_const);
67 builtin.ty(ctx.db, module) 73 builtin.ty(ctx.db, module)
68 } 74 }
69 _ => unreachable!(), 75 _ => unreachable!(),
@@ -92,9 +98,8 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
92 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 98 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
93 return None; 99 return None;
94 } 100 }
95 match item { 101 if let hir::AssocItem::TypeAlias(ty) = item {
96 hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} 102 acc.add_type_alias(ctx, ty)
97 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
98 } 103 }
99 None::<()> 104 None::<()>
100 }); 105 });
@@ -122,9 +127,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
122 }; 127 };
123 128
124 if let Some(Adt::Enum(e)) = ty.as_adt() { 129 if let Some(Adt::Enum(e)) = ty.as_adt() {
125 for variant in e.variants(ctx.db) { 130 add_enum_variants(ctx, acc, e);
126 acc.add_enum_variant(ctx, variant, None);
127 }
128 } 131 }
129 132
130 let traits_in_scope = ctx.scope.traits_in_scope(); 133 let traits_in_scope = ctx.scope.traits_in_scope();
@@ -151,6 +154,12 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
151 } 154 }
152} 155}
153 156
157fn add_enum_variants(ctx: &CompletionContext, acc: &mut Completions, e: hir::Enum) {
158 for variant in e.variants(ctx.db) {
159 acc.add_enum_variant(ctx, variant, None);
160 }
161}
162
154#[cfg(test)] 163#[cfg(test)]
155mod tests { 164mod tests {
156 use expect_test::{expect, Expect}; 165 use expect_test::{expect, Expect};
@@ -737,29 +746,7 @@ fn f() {}
737 } 746 }
738 747
739 #[test] 748 #[test]
740 fn completes_function() { 749 fn completes_variant_through_self() {
741 check(
742 r#"
743fn foo(
744 a: i32,
745 b: i32
746) {
747
748}
749
750fn main() {
751 fo$0
752}
753"#,
754 expect![[r#"
755 fn main() fn()
756 fn foo(…) fn(i32, i32)
757 "#]],
758 );
759 }
760
761 #[test]
762 fn completes_self_enum() {
763 check( 750 check(
764 r#" 751 r#"
765enum Foo { 752enum Foo {
@@ -783,6 +770,7 @@ impl Foo {
783 770
784 #[test] 771 #[test]
785 fn completes_primitive_assoc_const() { 772 fn completes_primitive_assoc_const() {
773 cov_mark::check!(completes_primitive_assoc_const);
786 check( 774 check(
787 r#" 775 r#"
788//- /lib.rs crate:lib deps:core 776//- /lib.rs crate:lib deps:core
@@ -804,4 +792,23 @@ impl u8 {
804 "#]], 792 "#]],
805 ); 793 );
806 } 794 }
795
796 #[test]
797 fn completes_variant_through_alias() {
798 cov_mark::check!(completes_variant_through_alias);
799 check(
800 r#"
801enum Foo {
802 Bar
803}
804type Foo2 = Foo;
805fn main() {
806 Foo2::$0
807}
808"#,
809 expect![[r#"
810 ev Bar ()
811 "#]],
812 );
813 }
807} 814}
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index a26fe7c6c..968c0254d 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -36,7 +36,7 @@ use ide_db::{traits::get_missing_assoc_items, SymbolKind};
36use syntax::{ 36use syntax::{
37 ast::{self, edit, Impl}, 37 ast::{self, edit, Impl},
38 display::function_declaration, 38 display::function_declaration,
39 AstNode, SyntaxKind, SyntaxNode, TextRange, T, 39 AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T,
40}; 40};
41use text_edit::TextEdit; 41use text_edit::TextEdit;
42 42
@@ -154,8 +154,7 @@ fn add_function_impl(
154 } else { 154 } else {
155 CompletionItemKind::SymbolKind(SymbolKind::Function) 155 CompletionItemKind::SymbolKind(SymbolKind::Function)
156 }; 156 };
157 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end()); 157 let range = replacement_range(ctx, fn_def_node);
158
159 if let Some(src) = func.source(ctx.db) { 158 if let Some(src) = func.source(ctx.db) {
160 let function_decl = function_declaration(&src.value); 159 let function_decl = function_declaration(&src.value);
161 match ctx.config.snippet_cap { 160 match ctx.config.snippet_cap {
@@ -183,8 +182,7 @@ fn add_type_alias_impl(
183 182
184 let snippet = format!("type {} = ", alias_name); 183 let snippet = format!("type {} = ", alias_name);
185 184
186 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end()); 185 let range = replacement_range(ctx, type_def_node);
187
188 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); 186 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
189 item.text_edit(TextEdit::replace(range, snippet)) 187 item.text_edit(TextEdit::replace(range, snippet))
190 .lookup_by(alias_name) 188 .lookup_by(alias_name)
@@ -205,9 +203,7 @@ fn add_const_impl(
205 if let Some(source) = const_.source(ctx.db) { 203 if let Some(source) = const_.source(ctx.db) {
206 let snippet = make_const_compl_syntax(&source.value); 204 let snippet = make_const_compl_syntax(&source.value);
207 205
208 let range = 206 let range = replacement_range(ctx, const_def_node);
209 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
210
211 let mut item = 207 let mut item =
212 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); 208 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
213 item.text_edit(TextEdit::replace(range, snippet)) 209 item.text_edit(TextEdit::replace(range, snippet))
@@ -242,6 +238,17 @@ fn make_const_compl_syntax(const_: &ast::Const) -> String {
242 format!("{} = ", syntax.trim_end()) 238 format!("{} = ", syntax.trim_end())
243} 239}
244 240
241fn replacement_range(ctx: &CompletionContext, item: &SyntaxNode) -> TextRange {
242 let first_child = item
243 .children_with_tokens()
244 .find(|child| {
245 !matches!(child.kind(), SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR)
246 })
247 .unwrap_or_else(|| SyntaxElement::Node(item.clone()));
248
249 TextRange::new(first_child.text_range().start(), ctx.source_range().end())
250}
251
245#[cfg(test)] 252#[cfg(test)]
246mod tests { 253mod tests {
247 use expect_test::{expect, Expect}; 254 use expect_test::{expect, Expect};
@@ -734,4 +741,50 @@ impl Test for T {{
734 test("CONST", "const $0", "const CONST: u16 = ", next_sibling); 741 test("CONST", "const $0", "const CONST: u16 = ", next_sibling);
735 } 742 }
736 } 743 }
744
745 #[test]
746 fn snippet_does_not_overwrite_comment_or_attr() {
747 let test = |completion: &str, hint: &str, completed: &str| {
748 check_edit(
749 completion,
750 &format!(
751 r#"
752trait Foo {{
753 type Type;
754 fn function();
755 const CONST: i32 = 0;
756}}
757struct T;
758
759impl Foo for T {{
760 // Comment
761 #[bar]
762 {}
763}}
764"#,
765 hint
766 ),
767 &format!(
768 r#"
769trait Foo {{
770 type Type;
771 fn function();
772 const CONST: i32 = 0;
773}}
774struct T;
775
776impl Foo for T {{
777 // Comment
778 #[bar]
779 {}
780}}
781"#,
782 completed
783 ),
784 )
785 };
786 test("function", "fn f$0", "fn function() {\n $0\n}");
787 test("Type", "type T$0", "type Type = ");
788 test("CONST", "const C$0", "const CONST: i32 = ");
789 }
737} 790}
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index 1b8b063e7..7875500c1 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -17,8 +17,10 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
17 return; 17 return;
18 } 18 }
19 19
20 if let Some(ty) = &ctx.expected_type { 20 if let Some(hir::Adt::Enum(e)) =
21 super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| { 21 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
22 {
23 super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| {
22 acc.add_qualified_enum_variant(ctx, variant, path) 24 acc.add_qualified_enum_variant(ctx, variant, path)
23 }); 25 });
24 } 26 }
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index b005bd773..787eb2fd3 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -301,103 +301,109 @@ impl<'a> CompletionContext<'a> {
301 .find_map(ast::Impl::cast); 301 .find_map(ast::Impl::cast);
302 } 302 }
303 303
304 fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
305 let mut node = match self.token.parent() {
306 Some(it) => it,
307 None => return (None, None),
308 };
309 loop {
310 break match_ast! {
311 match node {
312 ast::LetStmt(it) => {
313 cov_mark::hit!(expected_type_let_with_leading_char);
314 cov_mark::hit!(expected_type_let_without_leading_char);
315 let ty = it.pat()
316 .and_then(|pat| self.sema.type_of_pat(&pat))
317 .or_else(|| it.initializer().and_then(|it| self.sema.type_of_expr(&it)));
318 let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
319 ident.name().map(NameOrNameRef::Name)
320 } else {
321 None
322 };
323
324 (ty, name)
325 },
326 ast::ArgList(_it) => {
327 cov_mark::hit!(expected_type_fn_param_with_leading_char);
328 cov_mark::hit!(expected_type_fn_param_without_leading_char);
329 ActiveParameter::at_token(
330 &self.sema,
331 self.token.clone(),
332 ).map(|ap| {
333 let name = ap.ident().map(NameOrNameRef::Name);
334 (Some(ap.ty), name)
335 })
336 .unwrap_or((None, None))
337 },
338 ast::RecordExprFieldList(_it) => {
339 cov_mark::hit!(expected_type_struct_field_without_leading_char);
340 self.token.prev_sibling_or_token()
341 .and_then(|se| se.into_node())
342 .and_then(|node| ast::RecordExprField::cast(node))
343 .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
344 .map(|(f, rf)|(
345 Some(f.0.ty(self.db)),
346 rf.field_name().map(NameOrNameRef::NameRef),
347 ))
348 .unwrap_or((None, None))
349 },
350 ast::RecordExprField(it) => {
351 cov_mark::hit!(expected_type_struct_field_with_leading_char);
352 self.sema
353 .resolve_record_field(&it)
354 .map(|f|(
355 Some(f.0.ty(self.db)),
356 it.field_name().map(NameOrNameRef::NameRef),
357 ))
358 .unwrap_or((None, None))
359 },
360 ast::MatchExpr(it) => {
361 cov_mark::hit!(expected_type_match_arm_without_leading_char);
362 let ty = it.expr()
363 .and_then(|e| self.sema.type_of_expr(&e));
364 (ty, None)
365 },
366 ast::IfExpr(it) => {
367 cov_mark::hit!(expected_type_if_let_without_leading_char);
368 let ty = it.condition()
369 .and_then(|cond| cond.expr())
370 .and_then(|e| self.sema.type_of_expr(&e));
371 (ty, None)
372 },
373 ast::IdentPat(it) => {
374 cov_mark::hit!(expected_type_if_let_with_leading_char);
375 cov_mark::hit!(expected_type_match_arm_with_leading_char);
376 let ty = self.sema.type_of_pat(&ast::Pat::from(it));
377 (ty, None)
378 },
379 ast::Fn(it) => {
380 cov_mark::hit!(expected_type_fn_ret_with_leading_char);
381 cov_mark::hit!(expected_type_fn_ret_without_leading_char);
382 let def = self.sema.to_def(&it);
383 (def.map(|def| def.ret_type(self.db)), None)
384 },
385 ast::Stmt(_it) => (None, None),
386 _ => {
387 match node.parent() {
388 Some(n) => {
389 node = n;
390 continue;
391 },
392 None => (None, None),
393 }
394 },
395 }
396 };
397 }
398 }
399
304 fn fill( 400 fn fill(
305 &mut self, 401 &mut self,
306 original_file: &SyntaxNode, 402 original_file: &SyntaxNode,
307 file_with_fake_ident: SyntaxNode, 403 file_with_fake_ident: SyntaxNode,
308 offset: TextSize, 404 offset: TextSize,
309 ) { 405 ) {
310 let (expected_type, expected_name) = { 406 let (expected_type, expected_name) = self.expected_type_and_name();
311 let mut node = match self.token.parent() {
312 Some(it) => it,
313 None => return,
314 };
315 loop {
316 break match_ast! {
317 match node {
318 ast::LetStmt(it) => {
319 cov_mark::hit!(expected_type_let_with_leading_char);
320 cov_mark::hit!(expected_type_let_without_leading_char);
321 let ty = it.pat()
322 .and_then(|pat| self.sema.type_of_pat(&pat));
323 let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
324 ident.name().map(NameOrNameRef::Name)
325 } else {
326 None
327 };
328
329 (ty, name)
330 },
331 ast::ArgList(_it) => {
332 cov_mark::hit!(expected_type_fn_param_with_leading_char);
333 cov_mark::hit!(expected_type_fn_param_without_leading_char);
334 ActiveParameter::at_token(
335 &self.sema,
336 self.token.clone(),
337 ).map(|ap| {
338 let name = ap.ident().map(NameOrNameRef::Name);
339 (Some(ap.ty), name)
340 })
341 .unwrap_or((None, None))
342 },
343 ast::RecordExprFieldList(_it) => {
344 cov_mark::hit!(expected_type_struct_field_without_leading_char);
345 self.token.prev_sibling_or_token()
346 .and_then(|se| se.into_node())
347 .and_then(|node| ast::RecordExprField::cast(node))
348 .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
349 .map(|(f, rf)|(
350 Some(f.0.ty(self.db)),
351 rf.field_name().map(NameOrNameRef::NameRef),
352 ))
353 .unwrap_or((None, None))
354 },
355 ast::RecordExprField(it) => {
356 cov_mark::hit!(expected_type_struct_field_with_leading_char);
357 self.sema
358 .resolve_record_field(&it)
359 .map(|f|(
360 Some(f.0.ty(self.db)),
361 it.field_name().map(NameOrNameRef::NameRef),
362 ))
363 .unwrap_or((None, None))
364 },
365 ast::MatchExpr(it) => {
366 cov_mark::hit!(expected_type_match_arm_without_leading_char);
367 let ty = it.expr()
368 .and_then(|e| self.sema.type_of_expr(&e));
369
370 (ty, None)
371 },
372 ast::IdentPat(it) => {
373 cov_mark::hit!(expected_type_if_let_with_leading_char);
374 cov_mark::hit!(expected_type_match_arm_with_leading_char);
375 let ty = self.sema.type_of_pat(&ast::Pat::from(it));
376
377 (ty, None)
378 },
379 ast::Fn(_it) => {
380 cov_mark::hit!(expected_type_fn_ret_with_leading_char);
381 cov_mark::hit!(expected_type_fn_ret_without_leading_char);
382 let ty = self.token.ancestors()
383 .find_map(|ancestor| ast::Expr::cast(ancestor))
384 .and_then(|expr| self.sema.type_of_expr(&expr));
385
386 (ty, None)
387 },
388 _ => {
389 match node.parent() {
390 Some(n) => {
391 node = n;
392 continue;
393 },
394 None => (None, None),
395 }
396 },
397 }
398 };
399 }
400 };
401 self.expected_type = expected_type; 407 self.expected_type = expected_type;
402 self.expected_name = expected_name; 408 self.expected_name = expected_name;
403 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); 409 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
@@ -715,6 +721,26 @@ fn foo() {
715 } 721 }
716 722
717 #[test] 723 #[test]
724 fn expected_type_let_pat() {
725 check_expected_type_and_name(
726 r#"
727fn foo() {
728 let x$0 = 0u32;
729}
730"#,
731 expect![[r#"ty: u32, name: ?"#]],
732 );
733 check_expected_type_and_name(
734 r#"
735fn foo() {
736 let $0 = 0u32;
737}
738"#,
739 expect![[r#"ty: u32, name: ?"#]],
740 );
741 }
742
743 #[test]
718 fn expected_type_fn_param_without_leading_char() { 744 fn expected_type_fn_param_without_leading_char() {
719 cov_mark::check!(expected_type_fn_param_without_leading_char); 745 cov_mark::check!(expected_type_fn_param_without_leading_char);
720 check_expected_type_and_name( 746 check_expected_type_and_name(
@@ -802,6 +828,7 @@ fn foo() {
802 828
803 #[test] 829 #[test]
804 fn expected_type_if_let_without_leading_char() { 830 fn expected_type_if_let_without_leading_char() {
831 cov_mark::check!(expected_type_if_let_without_leading_char);
805 check_expected_type_and_name( 832 check_expected_type_and_name(
806 r#" 833 r#"
807enum Foo { Bar, Baz, Quux } 834enum Foo { Bar, Baz, Quux }
@@ -811,8 +838,8 @@ fn foo() {
811 if let $0 = f { } 838 if let $0 = f { }
812} 839}
813"#, 840"#,
814 expect![[r#"ty: (), name: ?"#]], 841 expect![[r#"ty: Foo, name: ?"#]],
815 ) // FIXME should be `ty: u32, name: ?` 842 )
816 } 843 }
817 844
818 #[test] 845 #[test]
@@ -840,8 +867,8 @@ fn foo() -> u32 {
840 $0 867 $0
841} 868}
842"#, 869"#,
843 expect![[r#"ty: (), name: ?"#]], 870 expect![[r#"ty: u32, name: ?"#]],
844 ) // FIXME this should be `ty: u32, name: ?` 871 )
845 } 872 }
846 873
847 #[test] 874 #[test]
@@ -856,4 +883,16 @@ fn foo() -> u32 {
856 expect![[r#"ty: u32, name: ?"#]], 883 expect![[r#"ty: u32, name: ?"#]],
857 ) 884 )
858 } 885 }
886
887 #[test]
888 fn expected_type_fn_ret_fn_ref_fully_typed() {
889 check_expected_type_and_name(
890 r#"
891fn foo() -> u32 {
892 foo$0
893}
894"#,
895 expect![[r#"ty: u32, name: ?"#]],
896 )
897 }
859} 898}
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index e32633565..645349215 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -80,7 +80,7 @@ pub use crate::{
80// 80//
81// And the auto import completions, enabled with the `rust-analyzer.completion.autoimport.enable` setting and the corresponding LSP client capabilities. 81// And the auto import completions, enabled with the `rust-analyzer.completion.autoimport.enable` setting and the corresponding LSP client capabilities.
82// Those are the additional completion options with automatic `use` import and options from all project importable items, 82// Those are the additional completion options with automatic `use` import and options from all project importable items,
83// fuzzy matched agains the completion imput. 83// fuzzy matched against the completion input.
84// 84//
85// image::https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif[] 85// image::https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif[]
86 86
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
index c9857ec5f..939fb2d47 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/test_utils.rs
@@ -20,7 +20,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
20 add_call_argument_snippets: true, 20 add_call_argument_snippets: true,
21 snippet_cap: SnippetCap::new(true), 21 snippet_cap: SnippetCap::new(true),
22 insert_use: InsertUseConfig { 22 insert_use: InsertUseConfig {
23 merge: Some(MergeBehavior::Full), 23 merge: Some(MergeBehavior::Crate),
24 prefix_kind: PrefixKind::Plain, 24 prefix_kind: PrefixKind::Plain,
25 group: true, 25 group: true,
26 }, 26 },