aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/base_db/src/fixture.rs10
-rw-r--r--crates/ide/src/fixture.rs17
-rw-r--r--crates/ide_completion/src/completions/keyword.rs192
-rw-r--r--crates/ide_completion/src/context.rs9
-rw-r--r--crates/ide_completion/src/patterns.rs12
-rw-r--r--crates/ide_completion/src/test_utils.rs16
-rw-r--r--crates/ide_db/src/call_info/tests.rs6
-rw-r--r--crates/ide_db/src/traits/tests.rs6
-rw-r--r--crates/test_utils/src/lib.rs15
9 files changed, 140 insertions, 143 deletions
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 0132565e4..69ceba735 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -34,19 +34,13 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
34 34
35 fn with_position(ra_fixture: &str) -> (Self, FilePosition) { 35 fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
36 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); 36 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
37 let offset = match range_or_offset { 37 let offset = range_or_offset.expect_offset();
38 RangeOrOffset::Range(_) => panic!("Expected a cursor position, got a range instead"),
39 RangeOrOffset::Offset(it) => it,
40 };
41 (db, FilePosition { file_id, offset }) 38 (db, FilePosition { file_id, offset })
42 } 39 }
43 40
44 fn with_range(ra_fixture: &str) -> (Self, FileRange) { 41 fn with_range(ra_fixture: &str) -> (Self, FileRange) {
45 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); 42 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
46 let range = match range_or_offset { 43 let range = range_or_offset.expect_range();
47 RangeOrOffset::Range(it) => it,
48 RangeOrOffset::Offset(_) => panic!("Expected a cursor range, got a position instead"),
49 };
50 (db, FileRange { file_id, range }) 44 (db, FileRange { file_id, range })
51 } 45 }
52 46
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs
index cc6641ba1..6780af617 100644
--- a/crates/ide/src/fixture.rs
+++ b/crates/ide/src/fixture.rs
@@ -1,7 +1,7 @@
1//! Utilities for creating `Analysis` instances for tests. 1//! Utilities for creating `Analysis` instances for tests.
2use ide_db::base_db::fixture::ChangeFixture; 2use ide_db::base_db::fixture::ChangeFixture;
3use syntax::{TextRange, TextSize}; 3use syntax::{TextRange, TextSize};
4use test_utils::{extract_annotations, RangeOrOffset}; 4use test_utils::extract_annotations;
5 5
6use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; 6use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
7 7
@@ -27,10 +27,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
27 let change_fixture = ChangeFixture::parse(ra_fixture); 27 let change_fixture = ChangeFixture::parse(ra_fixture);
28 host.db.apply_change(change_fixture.change); 28 host.db.apply_change(change_fixture.change);
29 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 29 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
30 let offset = match range_or_offset { 30 let offset = range_or_offset.expect_offset();
31 RangeOrOffset::Range(_) => panic!(),
32 RangeOrOffset::Offset(it) => it,
33 };
34 (host.analysis(), FilePosition { file_id, offset }) 31 (host.analysis(), FilePosition { file_id, offset })
35} 32}
36 33
@@ -40,10 +37,7 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
40 let change_fixture = ChangeFixture::parse(ra_fixture); 37 let change_fixture = ChangeFixture::parse(ra_fixture);
41 host.db.apply_change(change_fixture.change); 38 host.db.apply_change(change_fixture.change);
42 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 39 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
43 let range = match range_or_offset { 40 let range = range_or_offset.expect_range();
44 RangeOrOffset::Range(it) => it,
45 RangeOrOffset::Offset(_) => panic!(),
46 };
47 (host.analysis(), FileRange { file_id, range }) 41 (host.analysis(), FileRange { file_id, range })
48} 42}
49 43
@@ -53,10 +47,7 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
53 let change_fixture = ChangeFixture::parse(ra_fixture); 47 let change_fixture = ChangeFixture::parse(ra_fixture);
54 host.db.apply_change(change_fixture.change); 48 host.db.apply_change(change_fixture.change);
55 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 49 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
56 let offset = match range_or_offset { 50 let offset = range_or_offset.expect_offset();
57 RangeOrOffset::Range(_) => panic!(),
58 RangeOrOffset::Offset(it) => it,
59 };
60 51
61 let annotations = change_fixture 52 let annotations = change_fixture
62 .files 53 .files
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index c9673df85..662c389fe 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -39,6 +39,8 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
39 } 39 }
40} 40}
41 41
42trait Foo {}
43
42pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 44pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
43 if ctx.token.kind() == SyntaxKind::COMMENT { 45 if ctx.token.kind() == SyntaxKind::COMMENT {
44 cov_mark::hit!(no_keyword_completion_in_comments); 46 cov_mark::hit!(no_keyword_completion_in_comments);
@@ -48,91 +50,92 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
48 cov_mark::hit!(no_keyword_completion_in_record_lit); 50 cov_mark::hit!(no_keyword_completion_in_record_lit);
49 return; 51 return;
50 } 52 }
53 let mut add_keyword = |kw, snippet| add_keyword(ctx, acc, kw, snippet);
51 54
52 let expects_assoc_item = ctx.expects_assoc_item(); 55 let expects_assoc_item = ctx.expects_assoc_item();
53 let has_block_expr_parent = ctx.has_block_expr_parent(); 56 let has_block_expr_parent = ctx.has_block_expr_parent();
54 let expects_item = ctx.expects_item(); 57 let expects_item = ctx.expects_item();
58
55 if ctx.has_impl_or_trait_prev_sibling() { 59 if ctx.has_impl_or_trait_prev_sibling() {
56 add_keyword(ctx, acc, "where", "where "); 60 // FIXME this also incorrectly shows up after a complete trait/impl
61 add_keyword("where", "where ");
57 return; 62 return;
58 } 63 }
59 if ctx.previous_token_is(T![unsafe]) { 64 if ctx.previous_token_is(T![unsafe]) {
60 if expects_item || has_block_expr_parent { 65 if expects_item || expects_assoc_item || has_block_expr_parent {
61 add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}") 66 add_keyword("fn", "fn $1($2) {\n $0\n}")
62 } 67 }
63 68
64 if expects_item || has_block_expr_parent { 69 if expects_item || has_block_expr_parent {
65 add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); 70 add_keyword("trait", "trait $1 {\n $0\n}");
66 add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); 71 add_keyword("impl", "impl $1 {\n $0\n}");
67 } 72 }
68 73
69 return; 74 return;
70 } 75 }
76
77 if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() {
78 add_keyword("pub(crate)", "pub(crate) ");
79 add_keyword("pub", "pub ");
80 }
81
82 if expects_item || expects_assoc_item || has_block_expr_parent || ctx.is_match_arm {
83 add_keyword("unsafe", "unsafe ");
84 }
85
71 if expects_item || expects_assoc_item || has_block_expr_parent { 86 if expects_item || expects_assoc_item || has_block_expr_parent {
72 add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}"); 87 add_keyword("fn", "fn $1($2) {\n $0\n}");
88 add_keyword("const", "const $0");
89 add_keyword("type", "type $0");
73 } 90 }
91
74 if expects_item || has_block_expr_parent { 92 if expects_item || has_block_expr_parent {
75 add_keyword(ctx, acc, "use", "use "); 93 add_keyword("use", "use $0");
76 add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); 94 add_keyword("impl", "impl $1 {\n $0\n}");
77 add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); 95 add_keyword("trait", "trait $1 {\n $0\n}");
96 add_keyword("static", "static $0");
97 add_keyword("extern", "extern $0");
98 add_keyword("mod", "mod $0");
78 } 99 }
79 100
80 if expects_item { 101 if expects_item {
81 add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}"); 102 add_keyword("enum", "enum $1 {\n $0\n}");
82 add_keyword(ctx, acc, "struct", "struct $0"); 103 add_keyword("struct", "struct $0");
83 add_keyword(ctx, acc, "union", "union $1 {\n $0\n}"); 104 add_keyword("union", "union $1 {\n $0\n}");
84 } 105 }
85 106
86 if ctx.is_expr { 107 if ctx.expects_expression() {
87 add_keyword(ctx, acc, "match", "match $1 {\n $0\n}"); 108 add_keyword("match", "match $1 {\n $0\n}");
88 add_keyword(ctx, acc, "while", "while $1 {\n $0\n}"); 109 add_keyword("while", "while $1 {\n $0\n}");
89 add_keyword(ctx, acc, "while let", "while let $1 = $2 {\n $0\n}"); 110 add_keyword("while let", "while let $1 = $2 {\n $0\n}");
90 add_keyword(ctx, acc, "loop", "loop {\n $0\n}"); 111 add_keyword("loop", "loop {\n $0\n}");
91 add_keyword(ctx, acc, "if", "if $1 {\n $0\n}"); 112 add_keyword("if", "if $1 {\n $0\n}");
92 add_keyword(ctx, acc, "if let", "if let $1 = $2 {\n $0\n}"); 113 add_keyword("if let", "if let $1 = $2 {\n $0\n}");
93 add_keyword(ctx, acc, "for", "for $1 in $2 {\n $0\n}"); 114 add_keyword("for", "for $1 in $2 {\n $0\n}");
94 } 115 }
95 116
96 if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent { 117 if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent {
97 add_keyword(ctx, acc, "let", "let "); 118 add_keyword("let", "let ");
98 } 119 }
99 120
100 if ctx.after_if { 121 if ctx.after_if {
101 add_keyword(ctx, acc, "else", "else {\n $0\n}"); 122 add_keyword("else", "else {\n $0\n}");
102 add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}"); 123 add_keyword("else if", "else if $1 {\n $0\n}");
103 }
104 if expects_item || has_block_expr_parent {
105 add_keyword(ctx, acc, "mod", "mod $0");
106 } 124 }
125
107 if ctx.expects_ident_pat_or_ref_expr() { 126 if ctx.expects_ident_pat_or_ref_expr() {
108 add_keyword(ctx, acc, "mut", "mut "); 127 add_keyword("mut", "mut ");
109 }
110 if expects_item || expects_assoc_item || has_block_expr_parent {
111 add_keyword(ctx, acc, "const", "const ");
112 add_keyword(ctx, acc, "type", "type ");
113 }
114 if expects_item || has_block_expr_parent {
115 add_keyword(ctx, acc, "static", "static ");
116 };
117 if expects_item || has_block_expr_parent {
118 add_keyword(ctx, acc, "extern", "extern ");
119 }
120 if expects_item || expects_assoc_item || has_block_expr_parent || ctx.is_match_arm {
121 add_keyword(ctx, acc, "unsafe", "unsafe ");
122 } 128 }
129
123 if ctx.in_loop_body { 130 if ctx.in_loop_body {
124 if ctx.can_be_stmt { 131 if ctx.can_be_stmt {
125 add_keyword(ctx, acc, "continue", "continue;"); 132 add_keyword("continue", "continue;");
126 add_keyword(ctx, acc, "break", "break;"); 133 add_keyword("break", "break;");
127 } else { 134 } else {
128 add_keyword(ctx, acc, "continue", "continue"); 135 add_keyword("continue", "continue");
129 add_keyword(ctx, acc, "break", "break"); 136 add_keyword("break", "break");
130 } 137 }
131 } 138 }
132 if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() {
133 add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
134 add_keyword(ctx, acc, "pub", "pub ");
135 }
136 139
137 if !ctx.is_trivial_path { 140 if !ctx.is_trivial_path {
138 return; 141 return;
@@ -143,8 +146,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
143 }; 146 };
144 147
145 add_keyword( 148 add_keyword(
146 ctx,
147 acc,
148 "return", 149 "return",
149 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { 150 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) {
150 (true, true) => "return $0;", 151 (true, true) => "return $0;",
@@ -161,15 +162,12 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
161 162
162 match ctx.config.snippet_cap { 163 match ctx.config.snippet_cap {
163 Some(cap) => { 164 Some(cap) => {
164 let tmp; 165 if snippet.ends_with('}') && ctx.incomplete_let {
165 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
166 cov_mark::hit!(let_semi); 166 cov_mark::hit!(let_semi);
167 tmp = format!("{};", snippet); 167 item.insert_snippet(cap, format!("{};", snippet));
168 &tmp
169 } else { 168 } else {
170 snippet 169 item.insert_snippet(cap, snippet);
171 }; 170 }
172 item.insert_snippet(cap, snippet);
173 } 171 }
174 None => { 172 None => {
175 item.insert_text(if snippet.contains('$') { kw } else { snippet }); 173 item.insert_text(if snippet.contains('$') { kw } else { snippet });
@@ -232,21 +230,21 @@ mod tests {
232 check( 230 check(
233 r"m$0", 231 r"m$0",
234 expect![[r#" 232 expect![[r#"
233 kw pub(crate)
234 kw pub
235 kw unsafe
235 kw fn 236 kw fn
237 kw const
238 kw type
236 kw use 239 kw use
237 kw impl 240 kw impl
238 kw trait 241 kw trait
242 kw static
243 kw extern
244 kw mod
239 kw enum 245 kw enum
240 kw struct 246 kw struct
241 kw union 247 kw union
242 kw mod
243 kw const
244 kw type
245 kw static
246 kw extern
247 kw unsafe
248 kw pub(crate)
249 kw pub
250 "#]], 248 "#]],
251 ); 249 );
252 } 250 }
@@ -256,10 +254,16 @@ mod tests {
256 check( 254 check(
257 r"fn quux() { $0 }", 255 r"fn quux() { $0 }",
258 expect![[r#" 256 expect![[r#"
257 kw unsafe
259 kw fn 258 kw fn
259 kw const
260 kw type
260 kw use 261 kw use
261 kw impl 262 kw impl
262 kw trait 263 kw trait
264 kw static
265 kw extern
266 kw mod
263 kw match 267 kw match
264 kw while 268 kw while
265 kw while let 269 kw while let
@@ -268,12 +272,6 @@ mod tests {
268 kw if let 272 kw if let
269 kw for 273 kw for
270 kw let 274 kw let
271 kw mod
272 kw const
273 kw type
274 kw static
275 kw extern
276 kw unsafe
277 kw return 275 kw return
278 "#]], 276 "#]],
279 ); 277 );
@@ -284,10 +282,16 @@ mod tests {
284 check( 282 check(
285 r"fn quux() { if true { $0 } }", 283 r"fn quux() { if true { $0 } }",
286 expect![[r#" 284 expect![[r#"
285 kw unsafe
287 kw fn 286 kw fn
287 kw const
288 kw type
288 kw use 289 kw use
289 kw impl 290 kw impl
290 kw trait 291 kw trait
292 kw static
293 kw extern
294 kw mod
291 kw match 295 kw match
292 kw while 296 kw while
293 kw while let 297 kw while let
@@ -296,12 +300,6 @@ mod tests {
296 kw if let 300 kw if let
297 kw for 301 kw for
298 kw let 302 kw let
299 kw mod
300 kw const
301 kw type
302 kw static
303 kw extern
304 kw unsafe
305 kw return 303 kw return
306 "#]], 304 "#]],
307 ); 305 );
@@ -312,10 +310,16 @@ mod tests {
312 check( 310 check(
313 r#"fn quux() { if true { () } $0 }"#, 311 r#"fn quux() { if true { () } $0 }"#,
314 expect![[r#" 312 expect![[r#"
313 kw unsafe
315 kw fn 314 kw fn
315 kw const
316 kw type
316 kw use 317 kw use
317 kw impl 318 kw impl
318 kw trait 319 kw trait
320 kw static
321 kw extern
322 kw mod
319 kw match 323 kw match
320 kw while 324 kw while
321 kw while let 325 kw while let
@@ -326,12 +330,6 @@ mod tests {
326 kw let 330 kw let
327 kw else 331 kw else
328 kw else if 332 kw else if
329 kw mod
330 kw const
331 kw type
332 kw static
333 kw extern
334 kw unsafe
335 kw return 333 kw return
336 "#]], 334 "#]],
337 ); 335 );
@@ -353,6 +351,7 @@ fn quux() -> i32 {
353} 351}
354"#, 352"#,
355 expect![[r#" 353 expect![[r#"
354 kw unsafe
356 kw match 355 kw match
357 kw while 356 kw while
358 kw while let 357 kw while let
@@ -360,7 +359,6 @@ fn quux() -> i32 {
360 kw if 359 kw if
361 kw if let 360 kw if let
362 kw for 361 kw for
363 kw unsafe
364 kw return 362 kw return
365 "#]], 363 "#]],
366 ); 364 );
@@ -371,10 +369,10 @@ fn quux() -> i32 {
371 check( 369 check(
372 r"trait My { $0 }", 370 r"trait My { $0 }",
373 expect![[r#" 371 expect![[r#"
372 kw unsafe
374 kw fn 373 kw fn
375 kw const 374 kw const
376 kw type 375 kw type
377 kw unsafe
378 "#]], 376 "#]],
379 ); 377 );
380 } 378 }
@@ -384,12 +382,12 @@ fn quux() -> i32 {
384 check( 382 check(
385 r"impl My { $0 }", 383 r"impl My { $0 }",
386 expect![[r#" 384 expect![[r#"
385 kw pub(crate)
386 kw pub
387 kw unsafe
387 kw fn 388 kw fn
388 kw const 389 kw const
389 kw type 390 kw type
390 kw unsafe
391 kw pub(crate)
392 kw pub
393 "#]], 391 "#]],
394 ); 392 );
395 } 393 }
@@ -399,12 +397,12 @@ fn quux() -> i32 {
399 check( 397 check(
400 r"impl My { #[foo] $0 }", 398 r"impl My { #[foo] $0 }",
401 expect![[r#" 399 expect![[r#"
400 kw pub(crate)
401 kw pub
402 kw unsafe
402 kw fn 403 kw fn
403 kw const 404 kw const
404 kw type 405 kw type
405 kw unsafe
406 kw pub(crate)
407 kw pub
408 "#]], 406 "#]],
409 ); 407 );
410 } 408 }
@@ -414,10 +412,16 @@ fn quux() -> i32 {
414 check( 412 check(
415 r"fn my() { loop { $0 } }", 413 r"fn my() { loop { $0 } }",
416 expect![[r#" 414 expect![[r#"
415 kw unsafe
417 kw fn 416 kw fn
417 kw const
418 kw type
418 kw use 419 kw use
419 kw impl 420 kw impl
420 kw trait 421 kw trait
422 kw static
423 kw extern
424 kw mod
421 kw match 425 kw match
422 kw while 426 kw while
423 kw while let 427 kw while let
@@ -426,12 +430,6 @@ fn quux() -> i32 {
426 kw if let 430 kw if let
427 kw for 431 kw for
428 kw let 432 kw let
429 kw mod
430 kw const
431 kw type
432 kw static
433 kw extern
434 kw unsafe
435 kw continue 433 kw continue
436 kw break 434 kw break
437 kw return 435 kw return
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 923e35dbb..faf8469a5 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -288,6 +288,10 @@ impl<'a> CompletionContext<'a> {
288 matches!(self.completion_location, Some(ImmediateLocation::ItemList)) 288 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
289 } 289 }
290 290
291 pub(crate) fn expects_expression(&self) -> bool {
292 self.is_expr
293 }
294
291 pub(crate) fn has_block_expr_parent(&self) -> bool { 295 pub(crate) fn has_block_expr_parent(&self) -> bool {
292 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) 296 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
293 } 297 }
@@ -316,7 +320,7 @@ impl<'a> CompletionContext<'a> {
316 320
317 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { 321 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
318 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); 322 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
319 let syntax_element = NodeOrToken::Token(fake_ident_token.clone()); 323 let syntax_element = NodeOrToken::Token(fake_ident_token);
320 self.previous_token = previous_token(syntax_element.clone()); 324 self.previous_token = previous_token(syntax_element.clone());
321 self.in_loop_body = is_in_loop_body(syntax_element.clone()); 325 self.in_loop_body = is_in_loop_body(syntax_element.clone());
322 self.is_match_arm = is_match_arm(syntax_element.clone()); 326 self.is_match_arm = is_match_arm(syntax_element.clone());
@@ -338,8 +342,6 @@ impl<'a> CompletionContext<'a> {
338 let fn_is_prev = self.previous_token_is(T![fn]); 342 let fn_is_prev = self.previous_token_is(T![fn]);
339 let for_is_prev2 = for_is_prev2(syntax_element.clone()); 343 let for_is_prev2 = for_is_prev2(syntax_element.clone());
340 self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2; 344 self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2;
341
342 self.completion_location = determine_location(fake_ident_token);
343 } 345 }
344 346
345 fn fill_impl_def(&mut self) { 347 fn fill_impl_def(&mut self) {
@@ -465,6 +467,7 @@ impl<'a> CompletionContext<'a> {
465 Some(it) => it, 467 Some(it) => it,
466 None => return, 468 None => return,
467 }; 469 };
470 self.completion_location = determine_location(&name_like);
468 match name_like { 471 match name_like {
469 ast::NameLike::Lifetime(lifetime) => { 472 ast::NameLike::Lifetime(lifetime) => {
470 self.classify_lifetime(original_file, lifetime, offset); 473 self.classify_lifetime(original_file, lifetime, offset);
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index c8a88367d..f04471b57 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -24,12 +24,12 @@ pub(crate) enum ImmediateLocation {
24 ItemList, 24 ItemList,
25} 25}
26 26
27pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> { 27pub(crate) fn determine_location(name_like: &ast::NameLike) -> Option<ImmediateLocation> {
28 // First walk the element we are completing up to its highest node that has the same text range 28 // First walk the element we are completing up to its highest node that has the same text range
29 // as the element so that we can check in what context it immediately lies. We only do this for 29 // as the element so that we can check in what context it immediately lies. We only do this for
30 // NameRef -> Path as that's the only thing that makes sense to being "expanded" semantically. 30 // NameRef -> Path as that's the only thing that makes sense to being "expanded" semantically.
31 // We only wanna do this if the NameRef is the last segment of the path. 31 // We only wanna do this if the NameRef is the last segment of the path.
32 let node = match tok.parent().and_then(ast::NameLike::cast)? { 32 let node = match name_like {
33 ast::NameLike::NameRef(name_ref) => { 33 ast::NameLike::NameRef(name_ref) => {
34 if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) { 34 if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
35 let p = segment.parent_path(); 35 let p = segment.parent_path();
@@ -93,7 +93,8 @@ pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation>
93#[cfg(test)] 93#[cfg(test)]
94fn check_location(code: &str, loc: ImmediateLocation) { 94fn check_location(code: &str, loc: ImmediateLocation) {
95 check_pattern_is_applicable(code, |e| { 95 check_pattern_is_applicable(code, |e| {
96 assert_eq!(determine_location(e.into_token().expect("Expected a token")), Some(loc)); 96 let name = &e.parent().and_then(ast::NameLike::cast).expect("Expected a namelike");
97 assert_eq!(determine_location(name), Some(loc));
97 true 98 true
98 }); 99 });
99} 100}
@@ -199,6 +200,11 @@ fn test_has_impl_as_prev_sibling() {
199 check_pattern_is_applicable(r"impl A w$0 {}", |it| has_prev_sibling(it, IMPL)); 200 check_pattern_is_applicable(r"impl A w$0 {}", |it| has_prev_sibling(it, IMPL));
200} 201}
201 202
203#[test]
204fn test_has_trait_as_prev_sibling() {
205 check_pattern_is_applicable(r"trait A w$0 {}", |it| has_prev_sibling(it, TRAIT));
206}
207
202pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { 208pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
203 element 209 element
204 .ancestors() 210 .ancestors()
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
index 6656fd725..93c7c872c 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/test_utils.rs
@@ -12,7 +12,7 @@ use ide_db::{
12use itertools::Itertools; 12use itertools::Itertools;
13use stdx::{format_to, trim_indent}; 13use stdx::{format_to, trim_indent};
14use syntax::{AstNode, NodeOrToken, SyntaxElement}; 14use syntax::{AstNode, NodeOrToken, SyntaxElement};
15use test_utils::{assert_eq_text, RangeOrOffset}; 15use test_utils::assert_eq_text;
16 16
17use crate::{item::CompletionKind, CompletionConfig, CompletionItem}; 17use crate::{item::CompletionKind, CompletionConfig, CompletionItem};
18 18
@@ -36,10 +36,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
36 let mut database = RootDatabase::default(); 36 let mut database = RootDatabase::default();
37 database.apply_change(change_fixture.change); 37 database.apply_change(change_fixture.change);
38 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 38 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
39 let offset = match range_or_offset { 39 let offset = range_or_offset.expect_offset();
40 RangeOrOffset::Range(_) => panic!(),
41 RangeOrOffset::Offset(it) => it,
42 };
43 (database, FilePosition { file_id, offset }) 40 (database, FilePosition { file_id, offset })
44} 41}
45 42
@@ -52,10 +49,11 @@ pub(crate) fn do_completion_with_config(
52 code: &str, 49 code: &str,
53 kind: CompletionKind, 50 kind: CompletionKind,
54) -> Vec<CompletionItem> { 51) -> Vec<CompletionItem> {
55 let mut kind_completions: Vec<CompletionItem> = 52 get_all_items(config, code)
56 get_all_items(config, code).into_iter().filter(|c| c.completion_kind == kind).collect(); 53 .into_iter()
57 kind_completions.sort_by(|l, r| l.label().cmp(r.label())); 54 .filter(|c| c.completion_kind == kind)
58 kind_completions 55 .sorted_by(|l, r| l.label().cmp(r.label()))
56 .collect()
59} 57}
60 58
61pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { 59pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
index 1aeda08e5..b585085f3 100644
--- a/crates/ide_db/src/call_info/tests.rs
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -1,6 +1,5 @@
1use base_db::{fixture::ChangeFixture, FilePosition}; 1use base_db::{fixture::ChangeFixture, FilePosition};
2use expect_test::{expect, Expect}; 2use expect_test::{expect, Expect};
3use test_utils::RangeOrOffset;
4 3
5use crate::RootDatabase; 4use crate::RootDatabase;
6 5
@@ -10,10 +9,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
10 let mut database = RootDatabase::default(); 9 let mut database = RootDatabase::default();
11 database.apply_change(change_fixture.change); 10 database.apply_change(change_fixture.change);
12 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 11 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
13 let offset = match range_or_offset { 12 let offset = range_or_offset.expect_offset();
14 RangeOrOffset::Range(_) => panic!(),
15 RangeOrOffset::Offset(it) => it,
16 };
17 (database, FilePosition { file_id, offset }) 13 (database, FilePosition { file_id, offset })
18} 14}
19 15
diff --git a/crates/ide_db/src/traits/tests.rs b/crates/ide_db/src/traits/tests.rs
index 2a5482024..de994407c 100644
--- a/crates/ide_db/src/traits/tests.rs
+++ b/crates/ide_db/src/traits/tests.rs
@@ -2,7 +2,6 @@ use base_db::{fixture::ChangeFixture, FilePosition};
2use expect_test::{expect, Expect}; 2use expect_test::{expect, Expect};
3use hir::Semantics; 3use hir::Semantics;
4use syntax::ast::{self, AstNode}; 4use syntax::ast::{self, AstNode};
5use test_utils::RangeOrOffset;
6 5
7use crate::RootDatabase; 6use crate::RootDatabase;
8 7
@@ -12,10 +11,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
12 let mut database = RootDatabase::default(); 11 let mut database = RootDatabase::default();
13 database.apply_change(change_fixture.change); 12 database.apply_change(change_fixture.change);
14 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 13 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
15 let offset = match range_or_offset { 14 let offset = range_or_offset.expect_offset();
16 RangeOrOffset::Range(_) => panic!(),
17 RangeOrOffset::Offset(it) => it,
18 };
19 (database, FilePosition { file_id, offset }) 15 (database, FilePosition { file_id, offset })
20} 16}
21 17
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index fce4fd6bf..bd017567c 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -96,6 +96,21 @@ pub enum RangeOrOffset {
96 Offset(TextSize), 96 Offset(TextSize),
97} 97}
98 98
99impl RangeOrOffset {
100 pub fn expect_offset(self) -> TextSize {
101 match self {
102 RangeOrOffset::Offset(it) => it,
103 RangeOrOffset::Range(_) => panic!("expected an offset but got a range instead"),
104 }
105 }
106 pub fn expect_range(self) -> TextRange {
107 match self {
108 RangeOrOffset::Range(it) => it,
109 RangeOrOffset::Offset(_) => panic!("expected a range but got an offset"),
110 }
111 }
112}
113
99impl From<RangeOrOffset> for TextRange { 114impl From<RangeOrOffset> for TextRange {
100 fn from(selection: RangeOrOffset) -> Self { 115 fn from(selection: RangeOrOffset) -> Self {
101 match selection { 116 match selection {