aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src/context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src/context.rs')
-rw-r--r--crates/ide_completion/src/context.rs295
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)]
614mod 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#"
641fn 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#"
654fn 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#"
667fn foo() {
668 bar($0);
669}
670
671fn 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#"
682fn foo() {
683 bar(c$0);
684}
685
686fn 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#"
697struct Foo { a: u32 }
698fn 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#"
711struct Foo { a: u32 }
712fn 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#"
725enum E { X }
726fn 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#"
739enum E { X }
740fn 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#"
752enum Foo { Bar, Baz, Quux }
753
754fn 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#"
768enum Foo { Bar, Baz, Quux }
769
770fn 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#"
784fn 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#"
797fn foo() -> u32 {
798 c$0
799}
800"#,
801 expect![[r#"ty: u32, name: ?"#]],
802 )
803 }
804}