diff options
Diffstat (limited to 'crates/ide_completion/src/context.rs')
-rw-r--r-- | crates/ide_completion/src/context.rs | 156 |
1 files changed, 106 insertions, 50 deletions
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 4c3929a26..f0da98739 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -43,6 +43,8 @@ pub(crate) struct PathCompletionContext { | |||
43 | pub(super) is_trivial_path: bool, | 43 | pub(super) is_trivial_path: bool, |
44 | /// If not a trivial path, the prefix (qualifier). | 44 | /// If not a trivial path, the prefix (qualifier). |
45 | pub(super) qualifier: Option<ast::Path>, | 45 | pub(super) qualifier: Option<ast::Path>, |
46 | /// Whether the qualifier comes from a use tree parent or not | ||
47 | pub(super) use_tree_parent: bool, | ||
46 | pub(super) kind: Option<PathKind>, | 48 | pub(super) kind: Option<PathKind>, |
47 | /// Whether the path segment has type args or not. | 49 | /// Whether the path segment has type args or not. |
48 | pub(super) has_type_args: bool, | 50 | pub(super) has_type_args: bool, |
@@ -79,7 +81,6 @@ pub(crate) struct CompletionContext<'a> { | |||
79 | /// The parent impl of the cursor position if it exists. | 81 | /// The parent impl of the cursor position if it exists. |
80 | pub(super) impl_def: Option<ast::Impl>, | 82 | pub(super) impl_def: Option<ast::Impl>, |
81 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 83 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
82 | pub(super) use_item_syntax: Option<ast::Use>, | ||
83 | 84 | ||
84 | // potentially set if we are completing a lifetime | 85 | // potentially set if we are completing a lifetime |
85 | pub(super) lifetime_syntax: Option<ast::Lifetime>, | 86 | pub(super) lifetime_syntax: Option<ast::Lifetime>, |
@@ -151,7 +152,6 @@ impl<'a> CompletionContext<'a> { | |||
151 | function_def: None, | 152 | function_def: None, |
152 | impl_def: None, | 153 | impl_def: None, |
153 | name_ref_syntax: None, | 154 | name_ref_syntax: None, |
154 | use_item_syntax: None, | ||
155 | lifetime_syntax: None, | 155 | lifetime_syntax: None, |
156 | lifetime_param_syntax: None, | 156 | lifetime_param_syntax: None, |
157 | lifetime_allowed: false, | 157 | lifetime_allowed: false, |
@@ -242,32 +242,27 @@ impl<'a> CompletionContext<'a> { | |||
242 | } | 242 | } |
243 | 243 | ||
244 | pub(crate) fn expects_assoc_item(&self) -> bool { | 244 | pub(crate) fn expects_assoc_item(&self) -> bool { |
245 | matches!( | 245 | matches!(self.completion_location, Some(ImmediateLocation::Trait | ImmediateLocation::Impl)) |
246 | self.completion_location, | ||
247 | Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl) | ||
248 | ) | ||
249 | } | 246 | } |
250 | 247 | ||
251 | pub(crate) fn has_dot_receiver(&self) -> bool { | 248 | pub(crate) fn has_dot_receiver(&self) -> bool { |
252 | matches!( | 249 | matches!( |
253 | &self.completion_location, | 250 | &self.completion_location, |
254 | Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver,.. }) | 251 | Some(ImmediateLocation::FieldAccess { receiver, .. } | ImmediateLocation::MethodCall { receiver,.. }) |
255 | if receiver.is_some() | 252 | if receiver.is_some() |
256 | ) | 253 | ) |
257 | } | 254 | } |
258 | 255 | ||
259 | pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { | 256 | pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { |
260 | match &self.completion_location { | 257 | match &self.completion_location { |
261 | Some(ImmediateLocation::MethodCall { receiver, .. }) | 258 | Some( |
262 | | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(), | 259 | ImmediateLocation::MethodCall { receiver, .. } |
260 | | ImmediateLocation::FieldAccess { receiver, .. }, | ||
261 | ) => receiver.as_ref(), | ||
263 | _ => None, | 262 | _ => None, |
264 | } | 263 | } |
265 | } | 264 | } |
266 | 265 | ||
267 | pub(crate) fn expects_use_tree(&self) -> bool { | ||
268 | matches!(self.completion_location, Some(ImmediateLocation::Use)) | ||
269 | } | ||
270 | |||
271 | pub(crate) fn expects_non_trait_assoc_item(&self) -> bool { | 266 | pub(crate) fn expects_non_trait_assoc_item(&self) -> bool { |
272 | matches!(self.completion_location, Some(ImmediateLocation::Impl)) | 267 | matches!(self.completion_location, Some(ImmediateLocation::Impl)) |
273 | } | 268 | } |
@@ -276,6 +271,10 @@ impl<'a> CompletionContext<'a> { | |||
276 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) | 271 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) |
277 | } | 272 | } |
278 | 273 | ||
274 | pub(crate) fn expects_generic_arg(&self) -> bool { | ||
275 | matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_))) | ||
276 | } | ||
277 | |||
279 | pub(crate) fn has_block_expr_parent(&self) -> bool { | 278 | pub(crate) fn has_block_expr_parent(&self) -> bool { |
280 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) | 279 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) |
281 | } | 280 | } |
@@ -283,33 +282,59 @@ impl<'a> CompletionContext<'a> { | |||
283 | pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool { | 282 | pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool { |
284 | matches!( | 283 | matches!( |
285 | self.completion_location, | 284 | self.completion_location, |
286 | Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefExpr) | 285 | Some(ImmediateLocation::IdentPat | ImmediateLocation::RefExpr) |
287 | ) | 286 | ) |
288 | } | 287 | } |
289 | 288 | ||
290 | pub(crate) fn expect_record_field(&self) -> bool { | 289 | pub(crate) fn expect_field(&self) -> bool { |
291 | matches!(self.completion_location, Some(ImmediateLocation::RecordField)) | 290 | matches!( |
291 | self.completion_location, | ||
292 | Some(ImmediateLocation::RecordField | ImmediateLocation::TupleField) | ||
293 | ) | ||
294 | } | ||
295 | |||
296 | pub(crate) fn in_use_tree(&self) -> bool { | ||
297 | matches!( | ||
298 | self.completion_location, | ||
299 | Some(ImmediateLocation::Use | ImmediateLocation::UseTree) | ||
300 | ) | ||
292 | } | 301 | } |
293 | 302 | ||
294 | pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { | 303 | pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { |
295 | matches!( | 304 | matches!( |
296 | self.prev_sibling, | 305 | self.prev_sibling, |
297 | Some(ImmediatePrevSibling::ImplDefType) | Some(ImmediatePrevSibling::TraitDefName) | 306 | Some(ImmediatePrevSibling::ImplDefType | ImmediatePrevSibling::TraitDefName) |
298 | ) | 307 | ) |
299 | } | 308 | } |
300 | 309 | ||
310 | pub(crate) fn has_impl_prev_sibling(&self) -> bool { | ||
311 | matches!(self.prev_sibling, Some(ImmediatePrevSibling::ImplDefType)) | ||
312 | } | ||
313 | |||
314 | pub(crate) fn has_visibility_prev_sibling(&self) -> bool { | ||
315 | matches!(self.prev_sibling, Some(ImmediatePrevSibling::Visibility)) | ||
316 | } | ||
317 | |||
301 | pub(crate) fn after_if(&self) -> bool { | 318 | pub(crate) fn after_if(&self) -> bool { |
302 | matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr)) | 319 | matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr)) |
303 | } | 320 | } |
304 | 321 | ||
305 | pub(crate) fn is_path_disallowed(&self) -> bool { | 322 | pub(crate) fn is_path_disallowed(&self) -> bool { |
306 | matches!( | 323 | self.attribute_under_caret.is_some() |
307 | self.completion_location, | 324 | || self.previous_token_is(T![unsafe]) |
308 | Some(ImmediateLocation::Attribute(_)) | 325 | || matches!( |
309 | | Some(ImmediateLocation::ModDeclaration(_)) | 326 | self.prev_sibling, |
310 | | Some(ImmediateLocation::RecordPat(_)) | 327 | Some(ImmediatePrevSibling::Attribute | ImmediatePrevSibling::Visibility) |
311 | | Some(ImmediateLocation::RecordExpr(_)) | 328 | ) |
312 | ) || self.attribute_under_caret.is_some() | 329 | || matches!( |
330 | self.completion_location, | ||
331 | Some( | ||
332 | ImmediateLocation::Attribute(_) | ||
333 | | ImmediateLocation::ModDeclaration(_) | ||
334 | | ImmediateLocation::RecordPat(_) | ||
335 | | ImmediateLocation::RecordExpr(_) | ||
336 | ) | ||
337 | ) | ||
313 | } | 338 | } |
314 | 339 | ||
315 | pub(crate) fn expects_expression(&self) -> bool { | 340 | pub(crate) fn expects_expression(&self) -> bool { |
@@ -363,14 +388,19 @@ impl<'a> CompletionContext<'a> { | |||
363 | (ty, name) | 388 | (ty, name) |
364 | }, | 389 | }, |
365 | ast::ArgList(_it) => { | 390 | ast::ArgList(_it) => { |
366 | cov_mark::hit!(expected_type_fn_param_with_leading_char); | 391 | cov_mark::hit!(expected_type_fn_param); |
367 | cov_mark::hit!(expected_type_fn_param_without_leading_char); | ||
368 | ActiveParameter::at_token( | 392 | ActiveParameter::at_token( |
369 | &self.sema, | 393 | &self.sema, |
370 | self.token.clone(), | 394 | self.token.clone(), |
371 | ).map(|ap| { | 395 | ).map(|ap| { |
372 | let name = ap.ident().map(NameOrNameRef::Name); | 396 | let name = ap.ident().map(NameOrNameRef::Name); |
373 | (Some(ap.ty), name) | 397 | let ty = if has_ref(&self.token) { |
398 | cov_mark::hit!(expected_type_fn_param_ref); | ||
399 | ap.ty.remove_ref() | ||
400 | } else { | ||
401 | Some(ap.ty) | ||
402 | }; | ||
403 | (ty, name) | ||
374 | }) | 404 | }) |
375 | .unwrap_or((None, None)) | 405 | .unwrap_or((None, None)) |
376 | }, | 406 | }, |
@@ -564,9 +594,6 @@ impl<'a> CompletionContext<'a> { | |||
564 | self.name_ref_syntax = | 594 | self.name_ref_syntax = |
565 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); | 595 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); |
566 | 596 | ||
567 | self.use_item_syntax = | ||
568 | self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); | ||
569 | |||
570 | self.function_def = self | 597 | self.function_def = self |
571 | .sema | 598 | .sema |
572 | .token_ancestors_with_macros(self.token.clone()) | 599 | .token_ancestors_with_macros(self.token.clone()) |
@@ -586,6 +613,7 @@ impl<'a> CompletionContext<'a> { | |||
586 | has_type_args: false, | 613 | has_type_args: false, |
587 | can_be_stmt: false, | 614 | can_be_stmt: false, |
588 | in_loop_body: false, | 615 | in_loop_body: false, |
616 | use_tree_parent: false, | ||
589 | kind: None, | 617 | kind: None, |
590 | }); | 618 | }); |
591 | path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax()); | 619 | path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax()); |
@@ -613,7 +641,8 @@ impl<'a> CompletionContext<'a> { | |||
613 | } | 641 | } |
614 | path_ctx.has_type_args = segment.generic_arg_list().is_some(); | 642 | path_ctx.has_type_args = segment.generic_arg_list().is_some(); |
615 | 643 | ||
616 | if let Some(path) = path_or_use_tree_qualifier(&path) { | 644 | if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) { |
645 | path_ctx.use_tree_parent = use_tree_parent; | ||
617 | path_ctx.qualifier = path | 646 | path_ctx.qualifier = path |
618 | .segment() | 647 | .segment() |
619 | .and_then(|it| { | 648 | .and_then(|it| { |
@@ -667,13 +696,26 @@ fn is_node<N: AstNode>(node: &SyntaxNode) -> bool { | |||
667 | } | 696 | } |
668 | } | 697 | } |
669 | 698 | ||
670 | fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<ast::Path> { | 699 | fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> { |
671 | if let Some(qual) = path.qualifier() { | 700 | if let Some(qual) = path.qualifier() { |
672 | return Some(qual); | 701 | return Some((qual, false)); |
673 | } | 702 | } |
674 | let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?; | 703 | let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?; |
675 | let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?; | 704 | let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?; |
676 | use_tree.path() | 705 | use_tree.path().zip(Some(true)) |
706 | } | ||
707 | |||
708 | fn has_ref(token: &SyntaxToken) -> bool { | ||
709 | let mut token = token.clone(); | ||
710 | for skip in [WHITESPACE, IDENT, T![mut]] { | ||
711 | if token.kind() == skip { | ||
712 | token = match token.prev_token() { | ||
713 | Some(it) => it, | ||
714 | None => return false, | ||
715 | } | ||
716 | } | ||
717 | } | ||
718 | token.kind() == T![&] | ||
677 | } | 719 | } |
678 | 720 | ||
679 | #[cfg(test)] | 721 | #[cfg(test)] |
@@ -681,7 +723,7 @@ mod tests { | |||
681 | use expect_test::{expect, Expect}; | 723 | use expect_test::{expect, Expect}; |
682 | use hir::HirDisplay; | 724 | use hir::HirDisplay; |
683 | 725 | ||
684 | use crate::test_utils::{position, TEST_CONFIG}; | 726 | use crate::tests::{position, TEST_CONFIG}; |
685 | 727 | ||
686 | use super::CompletionContext; | 728 | use super::CompletionContext; |
687 | 729 | ||
@@ -748,14 +790,18 @@ fn foo() { | |||
748 | } | 790 | } |
749 | 791 | ||
750 | #[test] | 792 | #[test] |
751 | fn expected_type_fn_param_without_leading_char() { | 793 | fn expected_type_fn_param() { |
752 | cov_mark::check!(expected_type_fn_param_without_leading_char); | 794 | cov_mark::check!(expected_type_fn_param); |
753 | check_expected_type_and_name( | 795 | check_expected_type_and_name( |
754 | r#" | 796 | r#" |
755 | fn foo() { | 797 | fn foo() { bar($0); } |
756 | bar($0); | 798 | fn bar(x: u32) {} |
757 | } | 799 | "#, |
758 | 800 | expect![[r#"ty: u32, name: x"#]], | |
801 | ); | ||
802 | check_expected_type_and_name( | ||
803 | r#" | ||
804 | fn foo() { bar(c$0); } | ||
759 | fn bar(x: u32) {} | 805 | fn bar(x: u32) {} |
760 | "#, | 806 | "#, |
761 | expect![[r#"ty: u32, name: x"#]], | 807 | expect![[r#"ty: u32, name: x"#]], |
@@ -763,18 +809,29 @@ fn bar(x: u32) {} | |||
763 | } | 809 | } |
764 | 810 | ||
765 | #[test] | 811 | #[test] |
766 | fn expected_type_fn_param_with_leading_char() { | 812 | fn expected_type_fn_param_ref() { |
767 | cov_mark::check!(expected_type_fn_param_with_leading_char); | 813 | cov_mark::check!(expected_type_fn_param_ref); |
768 | check_expected_type_and_name( | 814 | check_expected_type_and_name( |
769 | r#" | 815 | r#" |
770 | fn foo() { | 816 | fn foo() { bar(&$0); } |
771 | bar(c$0); | 817 | fn bar(x: &u32) {} |
772 | } | 818 | "#, |
773 | 819 | expect![[r#"ty: u32, name: x"#]], | |
774 | fn bar(x: u32) {} | 820 | ); |
821 | check_expected_type_and_name( | ||
822 | r#" | ||
823 | fn foo() { bar(&mut $0); } | ||
824 | fn bar(x: &mut u32) {} | ||
775 | "#, | 825 | "#, |
776 | expect![[r#"ty: u32, name: x"#]], | 826 | expect![[r#"ty: u32, name: x"#]], |
777 | ); | 827 | ); |
828 | check_expected_type_and_name( | ||
829 | r#" | ||
830 | fn foo() { bar(&c$0); } | ||
831 | fn bar(x: &u32) {} | ||
832 | "#, | ||
833 | expect![[r#"ty: u32, name: x"#]], | ||
834 | ); | ||
778 | } | 835 | } |
779 | 836 | ||
780 | #[test] | 837 | #[test] |
@@ -921,13 +978,12 @@ fn foo() -> u32 { | |||
921 | // FIXME: make this work with `|| $0` | 978 | // FIXME: make this work with `|| $0` |
922 | check_expected_type_and_name( | 979 | check_expected_type_and_name( |
923 | r#" | 980 | r#" |
981 | //- minicore: fn | ||
924 | fn foo() { | 982 | fn foo() { |
925 | bar(|| a$0); | 983 | bar(|| a$0); |
926 | } | 984 | } |
927 | 985 | ||
928 | fn bar(f: impl FnOnce() -> u32) {} | 986 | fn bar(f: impl FnOnce() -> u32) {} |
929 | #[lang = "fn_once"] | ||
930 | trait FnOnce { type Output; } | ||
931 | "#, | 987 | "#, |
932 | expect![[r#"ty: u32, name: ?"#]], | 988 | expect![[r#"ty: u32, name: ?"#]], |
933 | ); | 989 | ); |