diff options
Diffstat (limited to 'crates/ide_completion/src/context.rs')
-rw-r--r-- | crates/ide_completion/src/context.rs | 295 |
1 files changed, 281 insertions, 14 deletions
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 17d9a3adf..e6cc6329c 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -35,6 +35,7 @@ pub(crate) struct CompletionContext<'a> { | |||
35 | /// The token before the cursor, in the macro-expanded file. | 35 | /// The token before the cursor, in the macro-expanded file. |
36 | pub(super) token: SyntaxToken, | 36 | pub(super) token: SyntaxToken, |
37 | pub(super) krate: Option<hir::Crate>, | 37 | pub(super) krate: Option<hir::Crate>, |
38 | pub(super) expected_name: Option<String>, | ||
38 | pub(super) expected_type: Option<Type>, | 39 | pub(super) expected_type: Option<Type>, |
39 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 40 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
40 | pub(super) function_syntax: Option<ast::Fn>, | 41 | pub(super) function_syntax: Option<ast::Fn>, |
@@ -135,6 +136,7 @@ impl<'a> CompletionContext<'a> { | |||
135 | original_token, | 136 | original_token, |
136 | token, | 137 | token, |
137 | krate, | 138 | krate, |
139 | expected_name: None, | ||
138 | expected_type: None, | 140 | expected_type: None, |
139 | name_ref_syntax: None, | 141 | name_ref_syntax: None, |
140 | function_syntax: None, | 142 | function_syntax: None, |
@@ -290,23 +292,95 @@ impl<'a> CompletionContext<'a> { | |||
290 | file_with_fake_ident: SyntaxNode, | 292 | file_with_fake_ident: SyntaxNode, |
291 | offset: TextSize, | 293 | offset: TextSize, |
292 | ) { | 294 | ) { |
293 | // FIXME: this is wrong in at least two cases: | 295 | let expected = { |
294 | // * when there's no token `foo($0)` | 296 | let mut node = self.token.parent(); |
295 | // * when there is a token, but it happens to have type of it's own | 297 | loop { |
296 | self.expected_type = self | 298 | let ret = match_ast! { |
297 | .token | ||
298 | .ancestors() | ||
299 | .find_map(|node| { | ||
300 | let ty = match_ast! { | ||
301 | match node { | 299 | match node { |
302 | ast::Pat(it) => self.sema.type_of_pat(&it), | 300 | ast::LetStmt(it) => { |
303 | ast::Expr(it) => self.sema.type_of_expr(&it), | 301 | cov_mark::hit!(expected_type_let_with_leading_char); |
304 | _ => return None, | 302 | cov_mark::hit!(expected_type_let_without_leading_char); |
303 | let ty = it.pat() | ||
304 | .and_then(|pat| self.sema.type_of_pat(&pat)); | ||
305 | let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() { | ||
306 | Some(ident.syntax().text().to_string()) | ||
307 | } else { | ||
308 | None | ||
309 | }; | ||
310 | |||
311 | (ty, name) | ||
312 | }, | ||
313 | ast::ArgList(it) => { | ||
314 | cov_mark::hit!(expected_type_fn_param_with_leading_char); | ||
315 | cov_mark::hit!(expected_type_fn_param_without_leading_char); | ||
316 | ActiveParameter::at_token( | ||
317 | &self.sema, | ||
318 | self.token.clone(), | ||
319 | ).map(|ap| (Some(ap.ty), Some(ap.name))) | ||
320 | .unwrap_or((None, None)) | ||
321 | }, | ||
322 | ast::RecordExprFieldList(it) => { | ||
323 | cov_mark::hit!(expected_type_struct_field_without_leading_char); | ||
324 | self.token.prev_sibling_or_token() | ||
325 | .and_then(|se| se.into_node()) | ||
326 | .and_then(|node| ast::RecordExprField::cast(node)) | ||
327 | .and_then(|rf| self.sema.resolve_record_field(&rf)) | ||
328 | .map(|f|( | ||
329 | Some(f.0.signature_ty(self.db)), | ||
330 | Some(f.0.name(self.db).to_string()), | ||
331 | )) | ||
332 | .unwrap_or((None, None)) | ||
333 | }, | ||
334 | ast::RecordExprField(it) => { | ||
335 | cov_mark::hit!(expected_type_struct_field_with_leading_char); | ||
336 | self.sema | ||
337 | .resolve_record_field(&it) | ||
338 | .map(|f|( | ||
339 | Some(f.0.signature_ty(self.db)), | ||
340 | Some(f.0.name(self.db).to_string()), | ||
341 | )) | ||
342 | .unwrap_or((None, None)) | ||
343 | }, | ||
344 | ast::MatchExpr(it) => { | ||
345 | cov_mark::hit!(expected_type_match_arm_without_leading_char); | ||
346 | let ty = it.expr() | ||
347 | .and_then(|e| self.sema.type_of_expr(&e)); | ||
348 | |||
349 | (ty, None) | ||
350 | }, | ||
351 | ast::IdentPat(it) => { | ||
352 | cov_mark::hit!(expected_type_if_let_with_leading_char); | ||
353 | cov_mark::hit!(expected_type_match_arm_with_leading_char); | ||
354 | let ty = self.sema.type_of_pat(&ast::Pat::from(it)); | ||
355 | |||
356 | (ty, None) | ||
357 | }, | ||
358 | ast::Fn(it) => { | ||
359 | cov_mark::hit!(expected_type_fn_ret_with_leading_char); | ||
360 | cov_mark::hit!(expected_type_fn_ret_without_leading_char); | ||
361 | let ty = self.token.ancestors() | ||
362 | .find_map(|ancestor| ast::Expr::cast(ancestor)) | ||
363 | .and_then(|expr| self.sema.type_of_expr(&expr)); | ||
364 | |||
365 | (ty, None) | ||
366 | }, | ||
367 | _ => { | ||
368 | match node.parent() { | ||
369 | Some(n) => { | ||
370 | node = n; | ||
371 | continue; | ||
372 | }, | ||
373 | None => (None, None), | ||
374 | } | ||
375 | }, | ||
305 | } | 376 | } |
306 | }; | 377 | }; |
307 | Some(ty) | 378 | |
308 | }) | 379 | break ret; |
309 | .flatten(); | 380 | } |
381 | }; | ||
382 | self.expected_type = expected.0; | ||
383 | self.expected_name = expected.1; | ||
310 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); | 384 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); |
311 | 385 | ||
312 | // First, let's try to complete a reference to some declaration. | 386 | // First, let's try to complete a reference to some declaration. |
@@ -535,3 +609,196 @@ fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<ast::Path> { | |||
535 | let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?; | 609 | let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?; |
536 | use_tree.path() | 610 | use_tree.path() |
537 | } | 611 | } |
612 | |||
613 | #[cfg(test)] | ||
614 | mod tests { | ||
615 | use expect_test::{expect, Expect}; | ||
616 | use hir::HirDisplay; | ||
617 | |||
618 | use crate::test_utils::{position, TEST_CONFIG}; | ||
619 | |||
620 | use super::CompletionContext; | ||
621 | |||
622 | fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) { | ||
623 | let (db, pos) = position(ra_fixture); | ||
624 | let completion_context = CompletionContext::new(&db, pos, &TEST_CONFIG).unwrap(); | ||
625 | |||
626 | let ty = completion_context | ||
627 | .expected_type | ||
628 | .map(|t| t.display_test(&db).to_string()) | ||
629 | .unwrap_or("?".to_owned()); | ||
630 | |||
631 | let name = completion_context.expected_name.unwrap_or("?".to_owned()); | ||
632 | |||
633 | expect.assert_eq(&format!("ty: {}, name: {}", ty, name)); | ||
634 | } | ||
635 | |||
636 | #[test] | ||
637 | fn expected_type_let_without_leading_char() { | ||
638 | cov_mark::check!(expected_type_let_without_leading_char); | ||
639 | check_expected_type_and_name( | ||
640 | r#" | ||
641 | fn foo() { | ||
642 | let x: u32 = $0; | ||
643 | } | ||
644 | "#, | ||
645 | expect![[r#"ty: u32, name: x"#]], | ||
646 | ); | ||
647 | } | ||
648 | |||
649 | #[test] | ||
650 | fn expected_type_let_with_leading_char() { | ||
651 | cov_mark::check!(expected_type_let_with_leading_char); | ||
652 | check_expected_type_and_name( | ||
653 | r#" | ||
654 | fn foo() { | ||
655 | let x: u32 = c$0; | ||
656 | } | ||
657 | "#, | ||
658 | expect![[r#"ty: u32, name: x"#]], | ||
659 | ); | ||
660 | } | ||
661 | |||
662 | #[test] | ||
663 | fn expected_type_fn_param_without_leading_char() { | ||
664 | cov_mark::check!(expected_type_fn_param_without_leading_char); | ||
665 | check_expected_type_and_name( | ||
666 | r#" | ||
667 | fn foo() { | ||
668 | bar($0); | ||
669 | } | ||
670 | |||
671 | fn bar(x: u32) {} | ||
672 | "#, | ||
673 | expect![[r#"ty: u32, name: x"#]], | ||
674 | ); | ||
675 | } | ||
676 | |||
677 | #[test] | ||
678 | fn expected_type_fn_param_with_leading_char() { | ||
679 | cov_mark::check!(expected_type_fn_param_with_leading_char); | ||
680 | check_expected_type_and_name( | ||
681 | r#" | ||
682 | fn foo() { | ||
683 | bar(c$0); | ||
684 | } | ||
685 | |||
686 | fn bar(x: u32) {} | ||
687 | "#, | ||
688 | expect![[r#"ty: u32, name: x"#]], | ||
689 | ); | ||
690 | } | ||
691 | |||
692 | #[test] | ||
693 | fn expected_type_struct_field_without_leading_char() { | ||
694 | cov_mark::check!(expected_type_struct_field_without_leading_char); | ||
695 | check_expected_type_and_name( | ||
696 | r#" | ||
697 | struct Foo { a: u32 } | ||
698 | fn foo() { | ||
699 | Foo { a: $0 }; | ||
700 | } | ||
701 | "#, | ||
702 | expect![[r#"ty: u32, name: a"#]], | ||
703 | ) | ||
704 | } | ||
705 | |||
706 | #[test] | ||
707 | fn expected_type_struct_field_with_leading_char() { | ||
708 | cov_mark::check!(expected_type_struct_field_with_leading_char); | ||
709 | check_expected_type_and_name( | ||
710 | r#" | ||
711 | struct Foo { a: u32 } | ||
712 | fn foo() { | ||
713 | Foo { a: c$0 }; | ||
714 | } | ||
715 | "#, | ||
716 | expect![[r#"ty: u32, name: a"#]], | ||
717 | ); | ||
718 | } | ||
719 | |||
720 | #[test] | ||
721 | fn expected_type_match_arm_without_leading_char() { | ||
722 | cov_mark::check!(expected_type_match_arm_without_leading_char); | ||
723 | check_expected_type_and_name( | ||
724 | r#" | ||
725 | enum E { X } | ||
726 | fn foo() { | ||
727 | match E::X { $0 } | ||
728 | } | ||
729 | "#, | ||
730 | expect![[r#"ty: E, name: ?"#]], | ||
731 | ); | ||
732 | } | ||
733 | |||
734 | #[test] | ||
735 | fn expected_type_match_arm_with_leading_char() { | ||
736 | cov_mark::check!(expected_type_match_arm_with_leading_char); | ||
737 | check_expected_type_and_name( | ||
738 | r#" | ||
739 | enum E { X } | ||
740 | fn foo() { | ||
741 | match E::X { c$0 } | ||
742 | } | ||
743 | "#, | ||
744 | expect![[r#"ty: E, name: ?"#]], | ||
745 | ); | ||
746 | } | ||
747 | |||
748 | #[test] | ||
749 | fn expected_type_if_let_without_leading_char() { | ||
750 | check_expected_type_and_name( | ||
751 | r#" | ||
752 | enum Foo { Bar, Baz, Quux } | ||
753 | |||
754 | fn foo() { | ||
755 | let f = Foo::Quux; | ||
756 | if let $0 = f { } | ||
757 | } | ||
758 | "#, | ||
759 | expect![[r#"ty: (), name: ?"#]], | ||
760 | ) // FIXME should be `ty: u32, name: ?` | ||
761 | } | ||
762 | |||
763 | #[test] | ||
764 | fn expected_type_if_let_with_leading_char() { | ||
765 | cov_mark::check!(expected_type_if_let_with_leading_char); | ||
766 | check_expected_type_and_name( | ||
767 | r#" | ||
768 | enum Foo { Bar, Baz, Quux } | ||
769 | |||
770 | fn foo() { | ||
771 | let f = Foo::Quux; | ||
772 | if let c$0 = f { } | ||
773 | } | ||
774 | "#, | ||
775 | expect![[r#"ty: Foo, name: ?"#]], | ||
776 | ) | ||
777 | } | ||
778 | |||
779 | #[test] | ||
780 | fn expected_type_fn_ret_without_leading_char() { | ||
781 | cov_mark::check!(expected_type_fn_ret_without_leading_char); | ||
782 | check_expected_type_and_name( | ||
783 | r#" | ||
784 | fn foo() -> u32 { | ||
785 | $0 | ||
786 | } | ||
787 | "#, | ||
788 | expect![[r#"ty: (), name: ?"#]], | ||
789 | ) // FIXME this should be `ty: u32, name: ?` | ||
790 | } | ||
791 | |||
792 | #[test] | ||
793 | fn expected_type_fn_ret_with_leading_char() { | ||
794 | cov_mark::check!(expected_type_fn_ret_with_leading_char); | ||
795 | check_expected_type_and_name( | ||
796 | r#" | ||
797 | fn foo() -> u32 { | ||
798 | c$0 | ||
799 | } | ||
800 | "#, | ||
801 | expect![[r#"ty: u32, name: ?"#]], | ||
802 | ) | ||
803 | } | ||
804 | } | ||