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.rs156
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
670fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<ast::Path> { 699fn 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
708fn 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#"
755fn foo() { 797fn foo() { bar($0); }
756 bar($0); 798fn bar(x: u32) {}
757} 799"#,
758 800 expect![[r#"ty: u32, name: x"#]],
801 );
802 check_expected_type_and_name(
803 r#"
804fn foo() { bar(c$0); }
759fn bar(x: u32) {} 805fn 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#"
770fn foo() { 816fn foo() { bar(&$0); }
771 bar(c$0); 817fn bar(x: &u32) {}
772} 818"#,
773 819 expect![[r#"ty: u32, name: x"#]],
774fn bar(x: u32) {} 820 );
821 check_expected_type_and_name(
822 r#"
823fn foo() { bar(&mut $0); }
824fn 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#"
830fn foo() { bar(&c$0); }
831fn 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
924fn foo() { 982fn foo() {
925 bar(|| a$0); 983 bar(|| a$0);
926} 984}
927 985
928fn bar(f: impl FnOnce() -> u32) {} 986fn bar(f: impl FnOnce() -> u32) {}
929#[lang = "fn_once"]
930trait FnOnce { type Output; }
931"#, 987"#,
932 expect![[r#"ty: u32, name: ?"#]], 988 expect![[r#"ty: u32, name: ?"#]],
933 ); 989 );