diff options
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r-- | crates/ide_completion/src/completions.rs | 56 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/pattern.rs | 6 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/postfix.rs | 5 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/qualified_path.rs | 73 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/trait_impl.rs | 69 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/unqualified_path.rs | 6 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 229 | ||||
-rw-r--r-- | crates/ide_completion/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ide_completion/src/test_utils.rs | 2 |
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 { | |||
203 | fn complete_enum_variants( | 203 | fn 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 | ||
157 | fn 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)] |
155 | mod tests { | 164 | mod 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#" | ||
743 | fn foo( | ||
744 | a: i32, | ||
745 | b: i32 | ||
746 | ) { | ||
747 | |||
748 | } | ||
749 | |||
750 | fn 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#" |
765 | enum Foo { | 752 | enum 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#" | ||
801 | enum Foo { | ||
802 | Bar | ||
803 | } | ||
804 | type Foo2 = Foo; | ||
805 | fn 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}; | |||
36 | use syntax::{ | 36 | use 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 | }; |
41 | use text_edit::TextEdit; | 41 | use 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 | ||
241 | fn 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)] |
246 | mod tests { | 253 | mod 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#" | ||
752 | trait Foo {{ | ||
753 | type Type; | ||
754 | fn function(); | ||
755 | const CONST: i32 = 0; | ||
756 | }} | ||
757 | struct T; | ||
758 | |||
759 | impl Foo for T {{ | ||
760 | // Comment | ||
761 | #[bar] | ||
762 | {} | ||
763 | }} | ||
764 | "#, | ||
765 | hint | ||
766 | ), | ||
767 | &format!( | ||
768 | r#" | ||
769 | trait Foo {{ | ||
770 | type Type; | ||
771 | fn function(); | ||
772 | const CONST: i32 = 0; | ||
773 | }} | ||
774 | struct T; | ||
775 | |||
776 | impl 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#" | ||
727 | fn foo() { | ||
728 | let x$0 = 0u32; | ||
729 | } | ||
730 | "#, | ||
731 | expect![[r#"ty: u32, name: ?"#]], | ||
732 | ); | ||
733 | check_expected_type_and_name( | ||
734 | r#" | ||
735 | fn 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#" |
807 | enum Foo { Bar, Baz, Quux } | 834 | enum 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#" | ||
891 | fn 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 | }, |