aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion')
-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/postfix/format_like.rs2
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs73
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs6
-rw-r--r--crates/ide_completion/src/context.rs208
-rw-r--r--crates/ide_completion/src/render/enum_variant.rs2
8 files changed, 190 insertions, 168 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/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs
index e86ffa8f8..0dcb3e898 100644
--- a/crates/ide_completion/src/completions/postfix/format_like.rs
+++ b/crates/ide_completion/src/completions/postfix/format_like.rs
@@ -1,4 +1,4 @@
1// Feature: Format String Completion. 1// Feature: Format String Completion
2// 2//
3// `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`. 3// `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`.
4// 4//
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/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 32f81aec1..f3fcb712c 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -301,103 +301,108 @@ 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 let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
318 ident.name().map(NameOrNameRef::Name)
319 } else {
320 None
321 };
322
323 (ty, name)
324 },
325 ast::ArgList(_it) => {
326 cov_mark::hit!(expected_type_fn_param_with_leading_char);
327 cov_mark::hit!(expected_type_fn_param_without_leading_char);
328 ActiveParameter::at_token(
329 &self.sema,
330 self.token.clone(),
331 ).map(|ap| {
332 let name = ap.ident().map(NameOrNameRef::Name);
333 (Some(ap.ty), name)
334 })
335 .unwrap_or((None, None))
336 },
337 ast::RecordExprFieldList(_it) => {
338 cov_mark::hit!(expected_type_struct_field_without_leading_char);
339 self.token.prev_sibling_or_token()
340 .and_then(|se| se.into_node())
341 .and_then(|node| ast::RecordExprField::cast(node))
342 .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
343 .map(|(f, rf)|(
344 Some(f.0.ty(self.db)),
345 rf.field_name().map(NameOrNameRef::NameRef),
346 ))
347 .unwrap_or((None, None))
348 },
349 ast::RecordExprField(it) => {
350 cov_mark::hit!(expected_type_struct_field_with_leading_char);
351 self.sema
352 .resolve_record_field(&it)
353 .map(|f|(
354 Some(f.0.ty(self.db)),
355 it.field_name().map(NameOrNameRef::NameRef),
356 ))
357 .unwrap_or((None, None))
358 },
359 ast::MatchExpr(it) => {
360 cov_mark::hit!(expected_type_match_arm_without_leading_char);
361 let ty = it.expr()
362 .and_then(|e| self.sema.type_of_expr(&e));
363 (ty, None)
364 },
365 ast::IfExpr(it) => {
366 cov_mark::hit!(expected_type_if_let_without_leading_char);
367 let ty = it.condition()
368 .and_then(|cond| cond.expr())
369 .and_then(|e| self.sema.type_of_expr(&e));
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 (ty, None)
377 },
378 ast::Fn(it) => {
379 cov_mark::hit!(expected_type_fn_ret_with_leading_char);
380 cov_mark::hit!(expected_type_fn_ret_without_leading_char);
381 let def = self.sema.to_def(&it);
382 (def.map(|def| def.ret_type(self.db)), None)
383 },
384 ast::Stmt(it) => (None, None),
385 _ => {
386 match node.parent() {
387 Some(n) => {
388 node = n;
389 continue;
390 },
391 None => (None, None),
392 }
393 },
394 }
395 };
396 }
397 }
398
304 fn fill( 399 fn fill(
305 &mut self, 400 &mut self,
306 original_file: &SyntaxNode, 401 original_file: &SyntaxNode,
307 file_with_fake_ident: SyntaxNode, 402 file_with_fake_ident: SyntaxNode,
308 offset: TextSize, 403 offset: TextSize,
309 ) { 404 ) {
310 let (expected_type, expected_name) = { 405 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.signature_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.signature_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; 406 self.expected_type = expected_type;
402 self.expected_name = expected_name; 407 self.expected_name = expected_name;
403 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); 408 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
@@ -802,6 +807,7 @@ fn foo() {
802 807
803 #[test] 808 #[test]
804 fn expected_type_if_let_without_leading_char() { 809 fn expected_type_if_let_without_leading_char() {
810 cov_mark::check!(expected_type_if_let_without_leading_char);
805 check_expected_type_and_name( 811 check_expected_type_and_name(
806 r#" 812 r#"
807enum Foo { Bar, Baz, Quux } 813enum Foo { Bar, Baz, Quux }
@@ -811,8 +817,8 @@ fn foo() {
811 if let $0 = f { } 817 if let $0 = f { }
812} 818}
813"#, 819"#,
814 expect![[r#"ty: (), name: ?"#]], 820 expect![[r#"ty: Foo, name: ?"#]],
815 ) // FIXME should be `ty: u32, name: ?` 821 )
816 } 822 }
817 823
818 #[test] 824 #[test]
@@ -840,8 +846,8 @@ fn foo() -> u32 {
840 $0 846 $0
841} 847}
842"#, 848"#,
843 expect![[r#"ty: (), name: ?"#]], 849 expect![[r#"ty: u32, name: ?"#]],
844 ) // FIXME this should be `ty: u32, name: ?` 850 )
845 } 851 }
846 852
847 #[test] 853 #[test]
@@ -856,4 +862,16 @@ fn foo() -> u32 {
856 expect![[r#"ty: u32, name: ?"#]], 862 expect![[r#"ty: u32, name: ?"#]],
857 ) 863 )
858 } 864 }
865
866 #[test]
867 fn expected_type_fn_ret_fn_ref_fully_typed() {
868 check_expected_type_and_name(
869 r#"
870fn foo() -> u32 {
871 foo$0
872}
873"#,
874 expect![[r#"ty: u32, name: ?"#]],
875 )
876 }
859} 877}
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
index 832f5ced1..0c0c71134 100644
--- a/crates/ide_completion/src/render/enum_variant.rs
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -93,7 +93,7 @@ impl<'a> EnumRender<'a> {
93 .variant 93 .variant
94 .fields(self.ctx.db()) 94 .fields(self.ctx.db())
95 .into_iter() 95 .into_iter()
96 .map(|field| (field.name(self.ctx.db()), field.signature_ty(self.ctx.db()))); 96 .map(|field| (field.name(self.ctx.db()), field.ty(self.ctx.db())));
97 97
98 match self.variant_kind { 98 match self.variant_kind {
99 StructKind::Tuple | StructKind::Unit => format!( 99 StructKind::Tuple | StructKind::Unit => format!(