aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-05-27 17:34:46 +0100
committerGitHub <[email protected]>2021-05-27 17:34:46 +0100
commita2940c42c0ab30e80e1a63494ca17fb2d81bdd1f (patch)
tree7d755c4edd86c0b031452eb45536eb42d802f65c
parentcc5d8069219a0a52f9c98b6766d2421eaf4664d8 (diff)
parent3a16950fd919f46fd879c36423810a40105b2c10 (diff)
Merge #9020
9020: fix: Don't complete non-macro item paths in impls and modules r=Veykril a=Veykril Part of #8518 bors r+ Co-authored-by: Lukas Wirth <[email protected]>
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs6
-rw-r--r--crates/ide_completion/src/completions/keyword.rs29
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs32
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs27
-rw-r--r--crates/ide_completion/src/context.rs63
-rw-r--r--crates/ide_completion/src/patterns.rs173
-rw-r--r--crates/ide_completion/src/test_utils.rs2
7 files changed, 198 insertions, 134 deletions
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index be9cfbded..df27e7a84 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -110,7 +110,11 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
110 if !ctx.config.enable_imports_on_the_fly { 110 if !ctx.config.enable_imports_on_the_fly {
111 return None; 111 return None;
112 } 112 }
113 if ctx.use_item_syntax.is_some() || ctx.is_path_disallowed() { 113 if ctx.use_item_syntax.is_some()
114 || ctx.is_path_disallowed()
115 || ctx.expects_item()
116 || ctx.expects_assoc_item()
117 {
114 return None; 118 return None;
115 } 119 }
116 let potential_import_name = { 120 let potential_import_name = {
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 58e35bad9..96447a603 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -49,35 +49,35 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
49 return; 49 return;
50 } 50 }
51 51
52 let has_trait_or_impl_parent = ctx.has_impl_or_trait_parent(); 52 let expects_assoc_item = ctx.expects_assoc_item();
53 let has_block_expr_parent = ctx.has_block_expr_parent(); 53 let has_block_expr_parent = ctx.has_block_expr_parent();
54 let has_item_list_parent = ctx.has_item_list_parent(); 54 let expects_item = ctx.expects_item();
55 if ctx.has_impl_or_trait_prev_sibling() { 55 if ctx.has_impl_or_trait_prev_sibling() {
56 add_keyword(ctx, acc, "where", "where "); 56 add_keyword(ctx, acc, "where", "where ");
57 return; 57 return;
58 } 58 }
59 if ctx.previous_token_is(T![unsafe]) { 59 if ctx.previous_token_is(T![unsafe]) {
60 if has_item_list_parent || has_block_expr_parent { 60 if expects_item || has_block_expr_parent {
61 add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}") 61 add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}")
62 } 62 }
63 63
64 if has_item_list_parent || has_block_expr_parent { 64 if expects_item || has_block_expr_parent {
65 add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); 65 add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}");
66 add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); 66 add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}");
67 } 67 }
68 68
69 return; 69 return;
70 } 70 }
71 if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent { 71 if expects_item || expects_assoc_item || has_block_expr_parent {
72 add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}"); 72 add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}");
73 } 73 }
74 if has_item_list_parent || has_block_expr_parent { 74 if expects_item || has_block_expr_parent {
75 add_keyword(ctx, acc, "use", "use "); 75 add_keyword(ctx, acc, "use", "use ");
76 add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); 76 add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}");
77 add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); 77 add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}");
78 } 78 }
79 79
80 if has_item_list_parent { 80 if expects_item {
81 add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}"); 81 add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}");
82 add_keyword(ctx, acc, "struct", "struct $0"); 82 add_keyword(ctx, acc, "struct", "struct $0");
83 add_keyword(ctx, acc, "union", "union $1 {\n $0\n}"); 83 add_keyword(ctx, acc, "union", "union $1 {\n $0\n}");
@@ -101,24 +101,23 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
101 add_keyword(ctx, acc, "else", "else {\n $0\n}"); 101 add_keyword(ctx, acc, "else", "else {\n $0\n}");
102 add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}"); 102 add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}");
103 } 103 }
104 if has_item_list_parent || has_block_expr_parent { 104 if expects_item || has_block_expr_parent {
105 add_keyword(ctx, acc, "mod", "mod $0"); 105 add_keyword(ctx, acc, "mod", "mod $0");
106 } 106 }
107 if ctx.has_ident_or_ref_pat_parent() { 107 if ctx.expects_ident_pat_or_ref_expr() {
108 add_keyword(ctx, acc, "mut", "mut "); 108 add_keyword(ctx, acc, "mut", "mut ");
109 } 109 }
110 if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent { 110 if expects_item || expects_assoc_item || has_block_expr_parent {
111 add_keyword(ctx, acc, "const", "const "); 111 add_keyword(ctx, acc, "const", "const ");
112 add_keyword(ctx, acc, "type", "type "); 112 add_keyword(ctx, acc, "type", "type ");
113 } 113 }
114 if has_item_list_parent || has_block_expr_parent { 114 if expects_item || has_block_expr_parent {
115 add_keyword(ctx, acc, "static", "static "); 115 add_keyword(ctx, acc, "static", "static ");
116 }; 116 };
117 if has_item_list_parent || has_block_expr_parent { 117 if expects_item || has_block_expr_parent {
118 add_keyword(ctx, acc, "extern", "extern "); 118 add_keyword(ctx, acc, "extern", "extern ");
119 } 119 }
120 if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent || ctx.is_match_arm 120 if expects_item || expects_assoc_item || has_block_expr_parent || ctx.is_match_arm {
121 {
122 add_keyword(ctx, acc, "unsafe", "unsafe "); 121 add_keyword(ctx, acc, "unsafe", "unsafe ");
123 } 122 }
124 if ctx.in_loop_body { 123 if ctx.in_loop_body {
@@ -130,7 +129,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
130 add_keyword(ctx, acc, "break", "break"); 129 add_keyword(ctx, acc, "break", "break");
131 } 130 }
132 } 131 }
133 if has_item_list_parent || ctx.has_impl_parent() || ctx.has_field_list_parent() { 132 if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() {
134 add_keyword(ctx, acc, "pub(crate)", "pub(crate) "); 133 add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
135 add_keyword(ctx, acc, "pub", "pub "); 134 add_keyword(ctx, acc, "pub", "pub ");
136 } 135 }
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index ed48f61af..c16bb215f 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -20,6 +20,17 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
20 None => return, 20 None => return,
21 }; 21 };
22 let context_module = ctx.scope.module(); 22 let context_module = ctx.scope.module();
23 if ctx.expects_item() || ctx.expects_assoc_item() {
24 if let PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
25 let module_scope = module.scope(ctx.db, context_module);
26 for (name, def) in module_scope {
27 if let ScopeDef::MacroDef(macro_def) = def {
28 acc.add_macro(ctx, Some(name.to_string()), macro_def);
29 }
30 }
31 }
32 return;
33 }
23 34
24 // Add associated types on type parameters and `Self`. 35 // Add associated types on type parameters and `Self`.
25 resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| { 36 resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
@@ -594,7 +605,7 @@ fn main() { T::$0; }
594macro_rules! foo { () => {} } 605macro_rules! foo { () => {} }
595 606
596fn main() { let _ = crate::$0 } 607fn main() { let _ = crate::$0 }
597 "#, 608"#,
598 expect![[r##" 609 expect![[r##"
599 fn main() fn() 610 fn main() fn()
600 ma foo!(…) #[macro_export] macro_rules! foo 611 ma foo!(…) #[macro_export] macro_rules! foo
@@ -603,6 +614,25 @@ fn main() { let _ = crate::$0 }
603 } 614 }
604 615
605 #[test] 616 #[test]
617 fn completes_qualified_macros_in_impl() {
618 check(
619 r#"
620#[macro_export]
621macro_rules! foo { () => {} }
622
623struct MyStruct {}
624
625impl MyStruct {
626 crate::$0
627}
628"#,
629 expect![[r##"
630 ma foo! #[macro_export] macro_rules! foo
631 "##]],
632 );
633 }
634
635 #[test]
606 fn test_super_super_completion() { 636 fn test_super_super_completion() {
607 check( 637 check(
608 r#" 638 r#"
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index 046a393ae..cbac88240 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -12,6 +12,14 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
12 if ctx.is_path_disallowed() { 12 if ctx.is_path_disallowed() {
13 return; 13 return;
14 } 14 }
15 if ctx.expects_item() || ctx.expects_assoc_item() {
16 ctx.scope.process_all_names(&mut |name, def| {
17 if let ScopeDef::MacroDef(macro_def) = def {
18 acc.add_macro(ctx, Some(name.to_string()), macro_def);
19 }
20 });
21 return;
22 }
15 23
16 if let Some(hir::Adt::Enum(e)) = 24 if let Some(hir::Adt::Enum(e)) =
17 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) 25 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
@@ -647,7 +655,7 @@ fn f() {}
647 } 655 }
648 656
649 #[test] 657 #[test]
650 fn completes_type_or_trait_in_impl_block() { 658 fn completes_target_type_or_trait_in_impl_block() {
651 check( 659 check(
652 r#" 660 r#"
653trait MyTrait {} 661trait MyTrait {}
@@ -662,4 +670,21 @@ impl My$0
662 "#]], 670 "#]],
663 ) 671 )
664 } 672 }
673
674 #[test]
675 fn only_completes_macros_in_assoc_item_list() {
676 check(
677 r#"
678struct MyStruct {}
679macro_rules! foo {}
680
681impl MyStruct {
682 $0
683}
684"#,
685 expect![[r#"
686 ma foo! macro_rules! foo
687 "#]],
688 )
689 }
665} 690}
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 66577df94..fbef54408 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -17,9 +17,8 @@ use text_edit::Indel;
17 17
18use crate::{ 18use crate::{
19 patterns::{ 19 patterns::{
20 for_is_prev2, has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, 20 determine_location, for_is_prev2, has_prev_sibling, inside_impl_trait_block,
21 has_impl_parent, has_item_list_or_source_file_parent, has_prev_sibling, has_ref_parent, 21 is_in_loop_body, is_match_arm, previous_token, ImmediateLocation,
22 has_trait_parent, inside_impl_trait_block, is_in_loop_body, is_match_arm, previous_token,
23 }, 22 },
24 CompletionConfig, 23 CompletionConfig,
25}; 24};
@@ -30,18 +29,6 @@ pub(crate) enum PatternRefutability {
30 Irrefutable, 29 Irrefutable,
31} 30}
32 31
33/// Direct parent container of the cursor position
34#[derive(Copy, Clone, Debug, PartialEq, Eq)]
35pub(crate) enum ImmediateLocation {
36 Impl,
37 Trait,
38 RecordFieldList,
39 RefPatOrExpr,
40 IdentPat,
41 BlockExpr,
42 ItemList,
43}
44
45#[derive(Copy, Clone, Debug, PartialEq, Eq)] 32#[derive(Copy, Clone, Debug, PartialEq, Eq)]
46pub(crate) enum PrevSibling { 33pub(crate) enum PrevSibling {
47 Trait, 34 Trait,
@@ -127,6 +114,7 @@ pub(crate) struct CompletionContext<'a> {
127 114
128 no_completion_required: bool, 115 no_completion_required: bool,
129} 116}
117
130impl<'a> CompletionContext<'a> { 118impl<'a> CompletionContext<'a> {
131 pub(super) fn new( 119 pub(super) fn new(
132 db: &'a RootDatabase, 120 db: &'a RootDatabase,
@@ -281,34 +269,34 @@ impl<'a> CompletionContext<'a> {
281 self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind) 269 self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
282 } 270 }
283 271
284 pub(crate) fn has_impl_or_trait_parent(&self) -> bool { 272 pub(crate) fn expects_assoc_item(&self) -> bool {
285 matches!( 273 matches!(
286 self.completion_location, 274 self.completion_location,
287 Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl) 275 Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl)
288 ) 276 )
289 } 277 }
290 278
291 pub(crate) fn has_block_expr_parent(&self) -> bool { 279 pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
292 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) 280 matches!(self.completion_location, Some(ImmediateLocation::Impl))
293 } 281 }
294 282
295 pub(crate) fn has_item_list_parent(&self) -> bool { 283 pub(crate) fn expects_item(&self) -> bool {
296 matches!(self.completion_location, Some(ImmediateLocation::ItemList)) 284 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
297 } 285 }
298 286
299 pub(crate) fn has_ident_or_ref_pat_parent(&self) -> bool { 287 pub(crate) fn has_block_expr_parent(&self) -> bool {
288 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
289 }
290
291 pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool {
300 matches!( 292 matches!(
301 self.completion_location, 293 self.completion_location,
302 Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefPatOrExpr) 294 Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefExpr)
303 ) 295 )
304 } 296 }
305 297
306 pub(crate) fn has_impl_parent(&self) -> bool { 298 pub(crate) fn expect_record_field(&self) -> bool {
307 matches!(self.completion_location, Some(ImmediateLocation::Impl)) 299 matches!(self.completion_location, Some(ImmediateLocation::RecordField))
308 }
309
310 pub(crate) fn has_field_list_parent(&self) -> bool {
311 matches!(self.completion_location, Some(ImmediateLocation::RecordFieldList))
312 } 300 }
313 301
314 pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { 302 pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
@@ -320,12 +308,11 @@ impl<'a> CompletionContext<'a> {
320 || self.record_pat_syntax.is_some() 308 || self.record_pat_syntax.is_some()
321 || self.attribute_under_caret.is_some() 309 || self.attribute_under_caret.is_some()
322 || self.mod_declaration_under_caret.is_some() 310 || self.mod_declaration_under_caret.is_some()
323 || self.has_impl_or_trait_parent()
324 } 311 }
325 312
326 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { 313 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
327 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); 314 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
328 let syntax_element = NodeOrToken::Token(fake_ident_token); 315 let syntax_element = NodeOrToken::Token(fake_ident_token.clone());
329 self.previous_token = previous_token(syntax_element.clone()); 316 self.previous_token = previous_token(syntax_element.clone());
330 self.in_loop_body = is_in_loop_body(syntax_element.clone()); 317 self.in_loop_body = is_in_loop_body(syntax_element.clone());
331 self.is_match_arm = is_match_arm(syntax_element.clone()); 318 self.is_match_arm = is_match_arm(syntax_element.clone());
@@ -335,22 +322,6 @@ impl<'a> CompletionContext<'a> {
335 self.prev_sibling = Some(PrevSibling::Trait) 322 self.prev_sibling = Some(PrevSibling::Trait)
336 } 323 }
337 324
338 if has_block_expr_parent(syntax_element.clone()) {
339 self.completion_location = Some(ImmediateLocation::BlockExpr);
340 } else if has_bind_pat_parent(syntax_element.clone()) {
341 self.completion_location = Some(ImmediateLocation::IdentPat);
342 } else if has_ref_parent(syntax_element.clone()) {
343 self.completion_location = Some(ImmediateLocation::RefPatOrExpr);
344 } else if has_impl_parent(syntax_element.clone()) {
345 self.completion_location = Some(ImmediateLocation::Impl);
346 } else if has_field_list_parent(syntax_element.clone()) {
347 self.completion_location = Some(ImmediateLocation::RecordFieldList);
348 } else if has_trait_parent(syntax_element.clone()) {
349 self.completion_location = Some(ImmediateLocation::Trait);
350 } else if has_item_list_or_source_file_parent(syntax_element.clone()) {
351 self.completion_location = Some(ImmediateLocation::ItemList);
352 }
353
354 self.mod_declaration_under_caret = 325 self.mod_declaration_under_caret =
355 find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) 326 find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
356 .filter(|module| module.item_list().is_none()); 327 .filter(|module| module.item_list().is_none());
@@ -363,6 +334,8 @@ impl<'a> CompletionContext<'a> {
363 let fn_is_prev = self.previous_token_is(T![fn]); 334 let fn_is_prev = self.previous_token_is(T![fn]);
364 let for_is_prev2 = for_is_prev2(syntax_element.clone()); 335 let for_is_prev2 = for_is_prev2(syntax_element.clone());
365 self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2; 336 self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2;
337
338 self.completion_location = determine_location(fake_ident_token);
366 } 339 }
367 340
368 fn fill_impl_def(&mut self) { 341 fn fill_impl_def(&mut self) {
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index 04f2c532b..ed289d561 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -11,96 +11,133 @@ use syntax::{
11#[cfg(test)] 11#[cfg(test)]
12use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; 12use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
13 13
14pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { 14/// Direct parent container of the cursor position
15 not_same_range_ancestor(element) 15#[derive(Copy, Clone, Debug, PartialEq, Eq)]
16 .filter(|it| it.kind() == ASSOC_ITEM_LIST) 16pub(crate) enum ImmediateLocation {
17 .and_then(|it| it.parent()) 17 Impl,
18 .filter(|it| it.kind() == TRAIT) 18 Trait,
19 .is_some() 19 RecordField,
20} 20 RefExpr,
21#[test] 21 IdentPat,
22fn test_has_trait_parent() { 22 BlockExpr,
23 check_pattern_is_applicable(r"trait A { f$0 }", has_trait_parent); 23 ItemList,
24}
25
26pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> {
27 // First "expand" the element we are completing to its maximum so that we can check in what
28 // context it immediately lies. This for example means if the token is a NameRef at the end of
29 // a path, we want to look at where the path is in the tree.
30 let node = match tok.parent().and_then(ast::NameLike::cast)? {
31 ast::NameLike::NameRef(name_ref) => {
32 if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
33 let p = segment.parent_path();
34 if p.parent_path().is_none() {
35 p.syntax()
36 .ancestors()
37 .take_while(|it| it.text_range() == p.syntax().text_range())
38 .last()?
39 } else {
40 return None;
41 }
42 } else {
43 return None;
44 }
45 }
46 it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(),
47 };
48 let parent = match node.parent() {
49 Some(parent) => parent,
50 // SourceFile
51 None => {
52 return match node.kind() {
53 MACRO_ITEMS | SOURCE_FILE => Some(ImmediateLocation::ItemList),
54 _ => None,
55 }
56 }
57 };
58 let res = match_ast! {
59 match parent {
60 ast::IdentPat(_it) => ImmediateLocation::IdentPat,
61 ast::BlockExpr(_it) => ImmediateLocation::BlockExpr,
62 ast::SourceFile(_it) => ImmediateLocation::ItemList,
63 ast::ItemList(_it) => ImmediateLocation::ItemList,
64 ast::RefExpr(_it) => ImmediateLocation::RefExpr,
65 ast::RefPat(_it) => ImmediateLocation::RefExpr,
66 ast::RecordField(_it) => ImmediateLocation::RecordField,
67 ast::AssocItemList(it) => match it.syntax().parent().map(|it| it.kind()) {
68 Some(IMPL) => ImmediateLocation::Impl,
69 Some(TRAIT) => ImmediateLocation::Trait,
70 _ => return None,
71 },
72 _ => return None,
73 }
74 };
75 Some(res)
24} 76}
25 77
26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { 78#[cfg(test)]
27 not_same_range_ancestor(element) 79fn check_location(code: &str, loc: ImmediateLocation) {
28 .filter(|it| it.kind() == ASSOC_ITEM_LIST) 80 check_pattern_is_applicable(code, |e| {
29 .and_then(|it| it.parent()) 81 assert_eq!(determine_location(e.into_token().expect("Expected a token")), Some(loc));
30 .filter(|it| it.kind() == IMPL) 82 true
31 .is_some() 83 });
32}
33#[test]
34fn test_has_impl_parent() {
35 check_pattern_is_applicable(r"impl A { f$0 }", has_impl_parent);
36} 84}
37 85
38pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
39 // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`,
40 // where we only check the first parent with different text range.
41 element
42 .ancestors()
43 .find(|it| it.kind() == IMPL)
44 .map(|it| ast::Impl::cast(it).unwrap())
45 .map(|it| it.trait_().is_some())
46 .unwrap_or(false)
47}
48#[test] 86#[test]
49fn test_inside_impl_trait_block() { 87fn test_has_trait_parent() {
50 check_pattern_is_applicable(r"impl Foo for Bar { f$0 }", inside_impl_trait_block); 88 check_location(r"trait A { f$0 }", ImmediateLocation::Trait);
51 check_pattern_is_applicable(r"impl Foo for Bar { fn f$0 }", inside_impl_trait_block);
52 check_pattern_is_not_applicable(r"impl A { f$0 }", inside_impl_trait_block);
53 check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block);
54} 89}
55 90
56pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool { 91#[test]
57 not_same_range_ancestor(element).filter(|it| it.kind() == RECORD_FIELD_LIST).is_some() 92fn test_has_impl_parent() {
93 check_location(r"impl A { f$0 }", ImmediateLocation::Impl);
58} 94}
59#[test] 95#[test]
60fn test_has_field_list_parent() { 96fn test_has_field_list_parent() {
61 check_pattern_is_applicable(r"struct Foo { f$0 }", has_field_list_parent); 97 check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField);
62 check_pattern_is_applicable(r"struct Foo { f$0 pub f: i32}", has_field_list_parent); 98 check_location(r"struct Foo { f$0 pub f: i32}", ImmediateLocation::RecordField);
63} 99}
64 100
65pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
66 not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some()
67}
68#[test] 101#[test]
69fn test_has_block_expr_parent() { 102fn test_has_block_expr_parent() {
70 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f$0 }", has_block_expr_parent); 103 check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::BlockExpr);
71} 104}
72 105
73pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool { 106#[test]
74 element.ancestors().any(|it| it.kind() == IDENT_PAT) 107fn test_has_ident_pat_parent() {
108 check_location(r"fn my_fn(m$0) {}", ImmediateLocation::IdentPat);
109 check_location(r"fn my_fn() { let m$0 }", ImmediateLocation::IdentPat);
110 check_location(r"fn my_fn(&m$0) {}", ImmediateLocation::IdentPat);
111 check_location(r"fn my_fn() { let &m$0 }", ImmediateLocation::IdentPat);
75} 112}
76 113
77#[test] 114#[test]
78fn test_has_bind_pat_parent() { 115fn test_has_ref_expr_parent() {
79 check_pattern_is_applicable(r"fn my_fn(m$0) {}", has_bind_pat_parent); 116 check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr);
80 check_pattern_is_applicable(r"fn my_fn() { let m$0 }", has_bind_pat_parent);
81} 117}
82 118
83pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
84 not_same_range_ancestor(element)
85 .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR)
86 .is_some()
87}
88#[test] 119#[test]
89fn test_has_ref_parent() { 120fn test_has_item_list_or_source_file_parent() {
90 check_pattern_is_applicable(r"fn my_fn(&m$0) {}", has_ref_parent); 121 check_location(r"i$0", ImmediateLocation::ItemList);
91 check_pattern_is_applicable(r"fn my() { let &m$0 }", has_ref_parent); 122 check_location(r"mod foo { f$0 }", ImmediateLocation::ItemList);
92} 123}
93 124
94pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool { 125pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
95 match not_same_range_ancestor(element) { 126 // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`,
96 Some(it) => it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST, 127 // where we only check the first parent with different text range.
97 None => true, 128 element
98 } 129 .ancestors()
130 .find(|it| it.kind() == IMPL)
131 .map(|it| ast::Impl::cast(it).unwrap())
132 .map(|it| it.trait_().is_some())
133 .unwrap_or(false)
99} 134}
100#[test] 135#[test]
101fn test_has_item_list_or_source_file_parent() { 136fn test_inside_impl_trait_block() {
102 check_pattern_is_applicable(r"i$0", has_item_list_or_source_file_parent); 137 check_pattern_is_applicable(r"impl Foo for Bar { f$0 }", inside_impl_trait_block);
103 check_pattern_is_applicable(r"mod foo { f$0 }", has_item_list_or_source_file_parent); 138 check_pattern_is_applicable(r"impl Foo for Bar { fn f$0 }", inside_impl_trait_block);
139 check_pattern_is_not_applicable(r"impl A { f$0 }", inside_impl_trait_block);
140 check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block);
104} 141}
105 142
106pub(crate) fn is_match_arm(element: SyntaxElement) -> bool { 143pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
@@ -160,12 +197,8 @@ pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
160 .is_some() 197 .is_some()
161} 198}
162 199
163fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> { 200pub(crate) fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
164 element 201 element.ancestors().skip_while(|it| it.text_range() == element.text_range()).next()
165 .ancestors()
166 .take_while(|it| it.text_range() == element.text_range())
167 .last()
168 .and_then(|it| it.parent())
169} 202}
170 203
171fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> { 204fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
index 37be575e5..6656fd725 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/test_utils.rs
@@ -132,7 +132,7 @@ pub(crate) fn check_edit_with_config(
132 assert_eq_text!(&ra_fixture_after, &actual) 132 assert_eq_text!(&ra_fixture_after, &actual)
133} 133}
134 134
135pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { 135pub(crate) fn check_pattern_is_applicable(code: &str, check: impl FnOnce(SyntaxElement) -> bool) {
136 let (db, pos) = position(code); 136 let (db, pos) = position(code);
137 137
138 let sema = Semantics::new(&db); 138 let sema = Semantics::new(&db);