aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-06-16 20:53:43 +0100
committerGitHub <[email protected]>2021-06-16 20:53:43 +0100
commitd6b8af44829521a9f925c4d87599efa3fef38edc (patch)
tree47119538effd381ecd8e15d422103512f2b47406
parentf38770cd2606148bfe764351849ea7ebea45132c (diff)
parentaa644b55859c6b5c6695a5d4fb35d1b6efbbebcc (diff)
Merge #9301
9301: internal: Start refactoring ide_completion tests r=Veykril a=Veykril Our current completion test infra resovles around usually just checking a specific `CompletionKind` which is suboptimal. We only see what we want to see in tests with this causing us to miss a lot of incorrect completions we are doing. Instead we should test for different cursor locations for all kinds(sans the magic kind maybe? not sure yet). This way we will also see potential duplicate completions that merely different in their kind. Also since most completion submodules complete things in tests of other modules due to the tests overlapping it makes more sense to group these tests differently which implies moving them to a new module. Exceptions for this might be stuff like attribute completion as these cannot currently interfere. I only wrote a few tests to check for completions in `ItemList` position so far and I already found a few incorrect/irrelevant completions as these haven't been tested properly due to them being hidden by the `CompletionKind` filtering. I think `CompletionKind` doesn't really seem to be beneficial to me as to I can't think of a occasion where we would want to only check a specific completion kind. Co-authored-by: Lukas Wirth <[email protected]>
-rw-r--r--crates/ide_completion/src/completions.rs113
-rw-r--r--crates/ide_completion/src/completions/attribute.rs4
-rw-r--r--crates/ide_completion/src/completions/attribute/derive.rs4
-rw-r--r--crates/ide_completion/src/completions/attribute/lint.rs2
-rw-r--r--crates/ide_completion/src/completions/dot.rs4
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs4
-rw-r--r--crates/ide_completion/src/completions/fn_param.rs4
-rw-r--r--crates/ide_completion/src/completions/keyword.rs109
-rw-r--r--crates/ide_completion/src/completions/lifetime.rs5
-rw-r--r--crates/ide_completion/src/completions/mod_.rs4
-rw-r--r--crates/ide_completion/src/completions/pattern.rs8
-rw-r--r--crates/ide_completion/src/completions/postfix.rs4
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs24
-rw-r--r--crates/ide_completion/src/completions/record.rs8
-rw-r--r--crates/ide_completion/src/completions/snippet.rs28
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs4
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs55
-rw-r--r--crates/ide_completion/src/context.rs26
-rw-r--r--crates/ide_completion/src/lib.rs123
-rw-r--r--crates/ide_completion/src/patterns.rs28
-rw-r--r--crates/ide_completion/src/render.rs2
-rw-r--r--crates/ide_completion/src/render/enum_variant.rs2
-rw-r--r--crates/ide_completion/src/render/function.rs2
-rw-r--r--crates/ide_completion/src/render/macro_.rs2
-rw-r--r--crates/ide_completion/src/tests.rs (renamed from crates/ide_completion/src/test_utils.rs)52
-rw-r--r--crates/ide_completion/src/tests/item_list.rs223
26 files changed, 430 insertions, 414 deletions
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index 783305005..cba5eb0c6 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -41,9 +41,9 @@ pub struct Completions {
41 buf: Vec<CompletionItem>, 41 buf: Vec<CompletionItem>,
42} 42}
43 43
44impl Into<Vec<CompletionItem>> for Completions { 44impl From<Completions> for Vec<CompletionItem> {
45 fn into(self) -> Vec<CompletionItem> { 45 fn from(val: Completions) -> Self {
46 self.buf 46 val.buf
47 } 47 }
48} 48}
49 49
@@ -74,35 +74,6 @@ impl Completions {
74 items.into_iter().for_each(|item| self.add(item.into())) 74 items.into_iter().for_each(|item| self.add(item.into()))
75 } 75 }
76 76
77 pub(crate) fn add_field(
78 &mut self,
79 ctx: &CompletionContext,
80 receiver: Option<hir::Name>,
81 field: hir::Field,
82 ty: &hir::Type,
83 ) {
84 let item = render_field(RenderContext::new(ctx), receiver, field, ty);
85 self.add(item);
86 }
87
88 pub(crate) fn add_tuple_field(
89 &mut self,
90 ctx: &CompletionContext,
91 receiver: Option<hir::Name>,
92 field: usize,
93 ty: &hir::Type,
94 ) {
95 let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
96 self.add(item);
97 }
98
99 pub(crate) fn add_static_lifetime(&mut self, ctx: &CompletionContext) {
100 let mut item =
101 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), "'static");
102 item.kind(CompletionItemKind::SymbolKind(SymbolKind::LifetimeParam));
103 self.add(item.build());
104 }
105
106 pub(crate) fn add_resolution( 77 pub(crate) fn add_resolution(
107 &mut self, 78 &mut self,
108 ctx: &CompletionContext, 79 ctx: &CompletionContext,
@@ -144,72 +115,102 @@ impl Completions {
144 self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func)); 115 self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func));
145 } 116 }
146 117
147 pub(crate) fn add_variant_pat( 118 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
119 self.add_opt(render_const(RenderContext::new(ctx), constant));
120 }
121
122 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
123 self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias));
124 }
125
126 pub(crate) fn add_type_alias_with_eq(
148 &mut self, 127 &mut self,
149 ctx: &CompletionContext, 128 ctx: &CompletionContext,
150 variant: hir::Variant, 129 type_alias: hir::TypeAlias,
151 local_name: Option<hir::Name>,
152 ) { 130 ) {
153 self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None)); 131 self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
154 } 132 }
155 133
156 pub(crate) fn add_qualified_variant_pat( 134 pub(crate) fn add_qualified_enum_variant(
157 &mut self, 135 &mut self,
158 ctx: &CompletionContext, 136 ctx: &CompletionContext,
159 variant: hir::Variant, 137 variant: hir::Variant,
160 path: hir::ModPath, 138 path: hir::ModPath,
161 ) { 139 ) {
162 self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path))); 140 let item = render_variant(RenderContext::new(ctx), None, None, variant, Some(path));
141 self.add(item);
163 } 142 }
164 143
165 pub(crate) fn add_struct_pat( 144 pub(crate) fn add_enum_variant(
166 &mut self, 145 &mut self,
167 ctx: &CompletionContext, 146 ctx: &CompletionContext,
168 strukt: hir::Struct, 147 variant: hir::Variant,
169 local_name: Option<hir::Name>, 148 local_name: Option<hir::Name>,
170 ) { 149 ) {
171 self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name)); 150 let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None);
151 self.add(item);
172 } 152 }
173 153
174 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { 154 pub(crate) fn add_field(
175 self.add_opt(render_const(RenderContext::new(ctx), constant)); 155 &mut self,
156 ctx: &CompletionContext,
157 receiver: Option<hir::Name>,
158 field: hir::Field,
159 ty: &hir::Type,
160 ) {
161 let item = render_field(RenderContext::new(ctx), receiver, field, ty);
162 self.add(item);
176 } 163 }
177 164
178 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) { 165 pub(crate) fn add_tuple_field(
179 self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias)); 166 &mut self,
167 ctx: &CompletionContext,
168 receiver: Option<hir::Name>,
169 field: usize,
170 ty: &hir::Type,
171 ) {
172 let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
173 self.add(item);
180 } 174 }
181 175
182 pub(crate) fn add_type_alias_with_eq( 176 pub(crate) fn add_static_lifetime(&mut self, ctx: &CompletionContext) {
177 let mut item =
178 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), "'static");
179 item.kind(CompletionItemKind::SymbolKind(SymbolKind::LifetimeParam));
180 self.add(item.build());
181 }
182
183 pub(crate) fn add_variant_pat(
183 &mut self, 184 &mut self,
184 ctx: &CompletionContext, 185 ctx: &CompletionContext,
185 type_alias: hir::TypeAlias, 186 variant: hir::Variant,
187 local_name: Option<hir::Name>,
186 ) { 188 ) {
187 self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias)); 189 self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None));
188 } 190 }
189 191
190 pub(crate) fn add_qualified_enum_variant( 192 pub(crate) fn add_qualified_variant_pat(
191 &mut self, 193 &mut self,
192 ctx: &CompletionContext, 194 ctx: &CompletionContext,
193 variant: hir::Variant, 195 variant: hir::Variant,
194 path: hir::ModPath, 196 path: hir::ModPath,
195 ) { 197 ) {
196 let item = render_variant(RenderContext::new(ctx), None, None, variant, Some(path)); 198 self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)));
197 self.add(item);
198 } 199 }
199 200
200 pub(crate) fn add_enum_variant( 201 pub(crate) fn add_struct_pat(
201 &mut self, 202 &mut self,
202 ctx: &CompletionContext, 203 ctx: &CompletionContext,
203 variant: hir::Variant, 204 strukt: hir::Struct,
204 local_name: Option<hir::Name>, 205 local_name: Option<hir::Name>,
205 ) { 206 ) {
206 let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); 207 self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name));
207 self.add(item);
208 } 208 }
209} 209}
210 210
211/// Calls the callback for each variant of the provided enum with the path to the variant. 211/// Calls the callback for each variant of the provided enum with the path to the variant.
212fn complete_enum_variants( 212/// Skips variants that are visible with single segment paths.
213fn enum_variants_with_paths(
213 acc: &mut Completions, 214 acc: &mut Completions,
214 ctx: &CompletionContext, 215 ctx: &CompletionContext,
215 enum_: hir::Enum, 216 enum_: hir::Enum,
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index 6df569c2a..3866c5917 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -322,7 +322,7 @@ mod tests {
322 322
323 use expect_test::{expect, Expect}; 323 use expect_test::{expect, Expect};
324 324
325 use crate::{test_utils::completion_list, CompletionKind}; 325 use crate::{tests::filtered_completion_list, CompletionKind};
326 326
327 #[test] 327 #[test]
328 fn attributes_are_sorted() { 328 fn attributes_are_sorted() {
@@ -341,7 +341,7 @@ mod tests {
341 } 341 }
342 342
343 fn check(ra_fixture: &str, expect: Expect) { 343 fn check(ra_fixture: &str, expect: Expect) {
344 let actual = completion_list(ra_fixture, CompletionKind::Attribute); 344 let actual = filtered_completion_list(ra_fixture, CompletionKind::Attribute);
345 expect.assert_eq(&actual); 345 expect.assert_eq(&actual);
346 } 346 }
347 347
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs
index 20bbbba46..5201095e8 100644
--- a/crates/ide_completion/src/completions/attribute/derive.rs
+++ b/crates/ide_completion/src/completions/attribute/derive.rs
@@ -82,7 +82,7 @@ const DEFAULT_DERIVE_COMPLETIONS: &[DeriveDependencies] = &[
82mod tests { 82mod tests {
83 use expect_test::{expect, Expect}; 83 use expect_test::{expect, Expect};
84 84
85 use crate::{test_utils::completion_list, CompletionKind}; 85 use crate::{tests::filtered_completion_list, CompletionKind};
86 86
87 fn check(ra_fixture: &str, expect: Expect) { 87 fn check(ra_fixture: &str, expect: Expect) {
88 let builtin_derives = r#" 88 let builtin_derives = r#"
@@ -106,7 +106,7 @@ pub macro PartialOrd {}
106pub macro Ord {} 106pub macro Ord {}
107 107
108"#; 108"#;
109 let actual = completion_list( 109 let actual = filtered_completion_list(
110 &format!("{} {}", builtin_derives, ra_fixture), 110 &format!("{} {}", builtin_derives, ra_fixture),
111 CompletionKind::Attribute, 111 CompletionKind::Attribute,
112 ); 112 );
diff --git a/crates/ide_completion/src/completions/attribute/lint.rs b/crates/ide_completion/src/completions/attribute/lint.rs
index ca99e9759..4812b075c 100644
--- a/crates/ide_completion/src/completions/attribute/lint.rs
+++ b/crates/ide_completion/src/completions/attribute/lint.rs
@@ -34,7 +34,7 @@ pub(super) fn complete_lint(
34#[cfg(test)] 34#[cfg(test)]
35mod tests { 35mod tests {
36 36
37 use crate::test_utils::check_edit; 37 use crate::tests::check_edit;
38 38
39 #[test] 39 #[test]
40 fn check_empty() { 40 fn check_empty() {
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index 9552875c1..7f75d4298 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -101,10 +101,10 @@ fn complete_methods(
101mod tests { 101mod tests {
102 use expect_test::{expect, Expect}; 102 use expect_test::{expect, Expect};
103 103
104 use crate::{test_utils::completion_list, CompletionKind}; 104 use crate::{tests::filtered_completion_list, CompletionKind};
105 105
106 fn check(ra_fixture: &str, expect: Expect) { 106 fn check(ra_fixture: &str, expect: Expect) {
107 let actual = completion_list(ra_fixture, CompletionKind::Reference); 107 let actual = filtered_completion_list(ra_fixture, CompletionKind::Reference);
108 expect.assert_eq(&actual); 108 expect.assert_eq(&actual);
109 } 109 }
110 110
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 30b8d44bd..4604feb5d 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -227,11 +227,11 @@ mod tests {
227 227
228 use crate::{ 228 use crate::{
229 item::CompletionKind, 229 item::CompletionKind,
230 test_utils::{check_edit, check_edit_with_config, completion_list, TEST_CONFIG}, 230 tests::{check_edit, check_edit_with_config, filtered_completion_list, TEST_CONFIG},
231 }; 231 };
232 232
233 fn check(ra_fixture: &str, expect: Expect) { 233 fn check(ra_fixture: &str, expect: Expect) {
234 let actual = completion_list(ra_fixture, CompletionKind::Magic); 234 let actual = filtered_completion_list(ra_fixture, CompletionKind::Magic);
235 expect.assert_eq(&actual); 235 expect.assert_eq(&actual);
236 } 236 }
237 237
diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs
index cb90e8a3e..c9f0e2473 100644
--- a/crates/ide_completion/src/completions/fn_param.rs
+++ b/crates/ide_completion/src/completions/fn_param.rs
@@ -64,10 +64,10 @@ pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
64mod tests { 64mod tests {
65 use expect_test::{expect, Expect}; 65 use expect_test::{expect, Expect};
66 66
67 use crate::{test_utils::completion_list, CompletionKind}; 67 use crate::{tests::filtered_completion_list, CompletionKind};
68 68
69 fn check(ra_fixture: &str, expect: Expect) { 69 fn check(ra_fixture: &str, expect: Expect) {
70 let actual = completion_list(ra_fixture, CompletionKind::Magic); 70 let actual = filtered_completion_list(ra_fixture, CompletionKind::Magic);
71 expect.assert_eq(&actual); 71 expect.assert_eq(&actual);
72 } 72 }
73 73
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 0fccbeccf..73bbc4345 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -75,7 +75,9 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
75 return; 75 return;
76 } 76 }
77 77
78 if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() { 78 if !ctx.has_visibility_prev_sibling()
79 && (expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field())
80 {
79 add_keyword("pub(crate)", "pub(crate) "); 81 add_keyword("pub(crate)", "pub(crate) ");
80 add_keyword("pub", "pub "); 82 add_keyword("pub", "pub ");
81 } 83 }
@@ -88,11 +90,13 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
88 } 90 }
89 91
90 if expects_item || has_block_expr_parent { 92 if expects_item || has_block_expr_parent {
93 if !ctx.has_visibility_prev_sibling() {
94 add_keyword("impl", "impl $1 {\n $0\n}");
95 add_keyword("extern", "extern $0");
96 }
91 add_keyword("use", "use $0"); 97 add_keyword("use", "use $0");
92 add_keyword("impl", "impl $1 {\n $0\n}");
93 add_keyword("trait", "trait $1 {\n $0\n}"); 98 add_keyword("trait", "trait $1 {\n $0\n}");
94 add_keyword("static", "static $0"); 99 add_keyword("static", "static $0");
95 add_keyword("extern", "extern $0");
96 add_keyword("mod", "mod $0"); 100 add_keyword("mod", "mod $0");
97 } 101 }
98 102
@@ -186,12 +190,12 @@ mod tests {
186 use expect_test::{expect, Expect}; 190 use expect_test::{expect, Expect};
187 191
188 use crate::{ 192 use crate::{
189 test_utils::{check_edit, completion_list}, 193 tests::{check_edit, filtered_completion_list},
190 CompletionKind, 194 CompletionKind,
191 }; 195 };
192 196
193 fn check(ra_fixture: &str, expect: Expect) { 197 fn check(ra_fixture: &str, expect: Expect) {
194 let actual = completion_list(ra_fixture, CompletionKind::Keyword); 198 let actual = filtered_completion_list(ra_fixture, CompletionKind::Keyword);
195 expect.assert_eq(&actual) 199 expect.assert_eq(&actual)
196 } 200 }
197 201
@@ -231,30 +235,6 @@ mod tests {
231 } 235 }
232 236
233 #[test] 237 #[test]
234 fn test_keywords_at_source_file_level() {
235 check(
236 r"m$0",
237 expect![[r#"
238 kw pub(crate)
239 kw pub
240 kw unsafe
241 kw fn
242 kw const
243 kw type
244 kw use
245 kw impl
246 kw trait
247 kw static
248 kw extern
249 kw mod
250 kw enum
251 kw struct
252 kw union
253 "#]],
254 );
255 }
256
257 #[test]
258 fn test_keywords_in_function() { 238 fn test_keywords_in_function() {
259 check( 239 check(
260 r"fn quux() { $0 }", 240 r"fn quux() { $0 }",
@@ -263,11 +243,11 @@ mod tests {
263 kw fn 243 kw fn
264 kw const 244 kw const
265 kw type 245 kw type
266 kw use
267 kw impl 246 kw impl
247 kw extern
248 kw use
268 kw trait 249 kw trait
269 kw static 250 kw static
270 kw extern
271 kw mod 251 kw mod
272 kw match 252 kw match
273 kw while 253 kw while
@@ -291,11 +271,11 @@ mod tests {
291 kw fn 271 kw fn
292 kw const 272 kw const
293 kw type 273 kw type
294 kw use
295 kw impl 274 kw impl
275 kw extern
276 kw use
296 kw trait 277 kw trait
297 kw static 278 kw static
298 kw extern
299 kw mod 279 kw mod
300 kw match 280 kw match
301 kw while 281 kw while
@@ -319,11 +299,11 @@ mod tests {
319 kw fn 299 kw fn
320 kw const 300 kw const
321 kw type 301 kw type
322 kw use
323 kw impl 302 kw impl
303 kw extern
304 kw use
324 kw trait 305 kw trait
325 kw static 306 kw static
326 kw extern
327 kw mod 307 kw mod
328 kw match 308 kw match
329 kw while 309 kw while
@@ -370,49 +350,6 @@ fn quux() -> i32 {
370 } 350 }
371 351
372 #[test] 352 #[test]
373 fn test_keywords_in_trait_def() {
374 check(
375 r"trait My { $0 }",
376 expect![[r#"
377 kw unsafe
378 kw fn
379 kw const
380 kw type
381 "#]],
382 );
383 }
384
385 #[test]
386 fn test_keywords_in_impl_def() {
387 check(
388 r"impl My { $0 }",
389 expect![[r#"
390 kw pub(crate)
391 kw pub
392 kw unsafe
393 kw fn
394 kw const
395 kw type
396 "#]],
397 );
398 }
399
400 #[test]
401 fn test_keywords_in_impl_def_with_attr() {
402 check(
403 r"impl My { #[foo] $0 }",
404 expect![[r#"
405 kw pub(crate)
406 kw pub
407 kw unsafe
408 kw fn
409 kw const
410 kw type
411 "#]],
412 );
413 }
414
415 #[test]
416 fn test_keywords_in_loop() { 353 fn test_keywords_in_loop() {
417 check( 354 check(
418 r"fn my() { loop { $0 } }", 355 r"fn my() { loop { $0 } }",
@@ -421,11 +358,11 @@ fn quux() -> i32 {
421 kw fn 358 kw fn
422 kw const 359 kw const
423 kw type 360 kw type
424 kw use
425 kw impl 361 kw impl
362 kw extern
363 kw use
426 kw trait 364 kw trait
427 kw static 365 kw static
428 kw extern
429 kw mod 366 kw mod
430 kw match 367 kw match
431 kw while 368 kw while
@@ -443,18 +380,6 @@ fn quux() -> i32 {
443 } 380 }
444 381
445 #[test] 382 #[test]
446 fn test_keywords_after_unsafe_in_item_list() {
447 check(
448 r"unsafe $0",
449 expect![[r#"
450 kw fn
451 kw trait
452 kw impl
453 "#]],
454 );
455 }
456
457 #[test]
458 fn test_keywords_after_unsafe_in_block_expr() { 383 fn test_keywords_after_unsafe_in_block_expr() {
459 check( 384 check(
460 r"fn my_fn() { unsafe $0 }", 385 r"fn my_fn() { unsafe $0 }",
diff --git a/crates/ide_completion/src/completions/lifetime.rs b/crates/ide_completion/src/completions/lifetime.rs
index 8ccccb646..36f595164 100644
--- a/crates/ide_completion/src/completions/lifetime.rs
+++ b/crates/ide_completion/src/completions/lifetime.rs
@@ -50,7 +50,7 @@ mod tests {
50 use expect_test::{expect, Expect}; 50 use expect_test::{expect, Expect};
51 51
52 use crate::{ 52 use crate::{
53 test_utils::{check_edit, completion_list_with_config, TEST_CONFIG}, 53 tests::{check_edit, filtered_completion_list_with_config, TEST_CONFIG},
54 CompletionConfig, CompletionKind, 54 CompletionConfig, CompletionKind,
55 }; 55 };
56 56
@@ -59,7 +59,8 @@ mod tests {
59 } 59 }
60 60
61 fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) { 61 fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) {
62 let actual = completion_list_with_config(config, ra_fixture, CompletionKind::Reference); 62 let actual =
63 filtered_completion_list_with_config(config, ra_fixture, CompletionKind::Reference);
63 expect.assert_eq(&actual) 64 expect.assert_eq(&actual)
64 } 65 }
65 66
diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs
index 6a5746fb9..5def0d06a 100644
--- a/crates/ide_completion/src/completions/mod_.rs
+++ b/crates/ide_completion/src/completions/mod_.rs
@@ -141,11 +141,11 @@ fn module_chain_to_containing_module_file(
141 141
142#[cfg(test)] 142#[cfg(test)]
143mod tests { 143mod tests {
144 use crate::{test_utils::completion_list, CompletionKind}; 144 use crate::{tests::filtered_completion_list, CompletionKind};
145 use expect_test::{expect, Expect}; 145 use expect_test::{expect, Expect};
146 146
147 fn check(ra_fixture: &str, expect: Expect) { 147 fn check(ra_fixture: &str, expect: Expect) {
148 let actual = completion_list(ra_fixture, CompletionKind::Magic); 148 let actual = filtered_completion_list(ra_fixture, CompletionKind::Magic);
149 expect.assert_eq(&actual); 149 expect.assert_eq(&actual);
150 } 150 }
151 151
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 1daa8595a..efe3c957a 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -13,7 +13,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
13 if let Some(hir::Adt::Enum(e)) = 13 if let Some(hir::Adt::Enum(e)) =
14 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) 14 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
15 { 15 {
16 super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| { 16 super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
17 acc.add_qualified_variant_pat(ctx, variant, path.clone()); 17 acc.add_qualified_variant_pat(ctx, variant, path.clone());
18 acc.add_qualified_enum_variant(ctx, variant, path); 18 acc.add_qualified_enum_variant(ctx, variant, path);
19 }); 19 });
@@ -61,17 +61,17 @@ mod tests {
61 use expect_test::{expect, Expect}; 61 use expect_test::{expect, Expect};
62 62
63 use crate::{ 63 use crate::{
64 test_utils::{check_edit, completion_list}, 64 tests::{check_edit, filtered_completion_list},
65 CompletionKind, 65 CompletionKind,
66 }; 66 };
67 67
68 fn check(ra_fixture: &str, expect: Expect) { 68 fn check(ra_fixture: &str, expect: Expect) {
69 let actual = completion_list(ra_fixture, CompletionKind::Reference); 69 let actual = filtered_completion_list(ra_fixture, CompletionKind::Reference);
70 expect.assert_eq(&actual) 70 expect.assert_eq(&actual)
71 } 71 }
72 72
73 fn check_snippet(ra_fixture: &str, expect: Expect) { 73 fn check_snippet(ra_fixture: &str, expect: Expect) {
74 let actual = completion_list(ra_fixture, CompletionKind::Snippet); 74 let actual = filtered_completion_list(ra_fixture, CompletionKind::Snippet);
75 expect.assert_eq(&actual) 75 expect.assert_eq(&actual)
76 } 76 }
77 77
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index 9f98b21be..c3c7e4589 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -307,12 +307,12 @@ mod tests {
307 use expect_test::{expect, Expect}; 307 use expect_test::{expect, Expect};
308 308
309 use crate::{ 309 use crate::{
310 test_utils::{check_edit, completion_list}, 310 tests::{check_edit, filtered_completion_list},
311 CompletionKind, 311 CompletionKind,
312 }; 312 };
313 313
314 fn check(ra_fixture: &str, expect: Expect) { 314 fn check(ra_fixture: &str, expect: Expect) {
315 let actual = completion_list(ra_fixture, CompletionKind::Postfix); 315 let actual = filtered_completion_list(ra_fixture, CompletionKind::Postfix);
316 expect.assert_eq(&actual) 316 expect.assert_eq(&actual)
317 } 317 }
318 318
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 1643eeed4..9432caa22 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -198,17 +198,17 @@ mod tests {
198 use expect_test::{expect, Expect}; 198 use expect_test::{expect, Expect};
199 199
200 use crate::{ 200 use crate::{
201 test_utils::{check_edit, completion_list}, 201 tests::{check_edit, filtered_completion_list},
202 CompletionKind, 202 CompletionKind,
203 }; 203 };
204 204
205 fn check(ra_fixture: &str, expect: Expect) { 205 fn check(ra_fixture: &str, expect: Expect) {
206 let actual = completion_list(ra_fixture, CompletionKind::Reference); 206 let actual = filtered_completion_list(ra_fixture, CompletionKind::Reference);
207 expect.assert_eq(&actual); 207 expect.assert_eq(&actual);
208 } 208 }
209 209
210 fn check_builtin(ra_fixture: &str, expect: Expect) { 210 fn check_builtin(ra_fixture: &str, expect: Expect) {
211 let actual = completion_list(ra_fixture, CompletionKind::BuiltinType); 211 let actual = filtered_completion_list(ra_fixture, CompletionKind::BuiltinType);
212 expect.assert_eq(&actual); 212 expect.assert_eq(&actual);
213 } 213 }
214 214
@@ -714,24 +714,6 @@ impl MyStruct {
714 } 714 }
715 715
716 #[test] 716 #[test]
717 fn completes_in_item_list() {
718 check(
719 r#"
720struct MyStruct {}
721#[macro_export]
722macro_rules! foo {}
723mod bar {}
724
725crate::$0
726"#,
727 expect![[r#"
728 md bar
729 ma foo!(…) #[macro_export] macro_rules! foo
730 "#]],
731 )
732 }
733
734 #[test]
735 fn test_super_super_completion() { 717 fn test_super_super_completion() {
736 check( 718 check(
737 r#" 719 r#"
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
index 0ac47cdbe..47523f72f 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -51,17 +51,17 @@ mod tests {
51 use ide_db::helpers::FamousDefs; 51 use ide_db::helpers::FamousDefs;
52 52
53 use crate::{ 53 use crate::{
54 test_utils::{self, completion_list}, 54 tests::{self, filtered_completion_list},
55 CompletionKind, 55 CompletionKind,
56 }; 56 };
57 57
58 fn check(ra_fixture: &str, expect: Expect) { 58 fn check(ra_fixture: &str, expect: Expect) {
59 let actual = completion_list(ra_fixture, CompletionKind::Reference); 59 let actual = filtered_completion_list(ra_fixture, CompletionKind::Reference);
60 expect.assert_eq(&actual); 60 expect.assert_eq(&actual);
61 } 61 }
62 62
63 fn check_snippet(ra_fixture: &str, expect: Expect) { 63 fn check_snippet(ra_fixture: &str, expect: Expect) {
64 let actual = completion_list( 64 let actual = filtered_completion_list(
65 &format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE), 65 &format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE),
66 CompletionKind::Snippet, 66 CompletionKind::Snippet,
67 ); 67 );
@@ -69,7 +69,7 @@ mod tests {
69 } 69 }
70 70
71 fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 71 fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
72 test_utils::check_edit( 72 tests::check_edit(
73 what, 73 what,
74 &format!( 74 &format!(
75 "//- /main.rs crate:main deps:core{}\n{}", 75 "//- /main.rs crate:main deps:core{}\n{}",
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index b9862de67..81ddfa34f 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -1,6 +1,7 @@
1//! This file provides snippet completions, like `pd` => `eprintln!(...)`. 1//! This file provides snippet completions, like `pd` => `eprintln!(...)`.
2 2
3use ide_db::helpers::SnippetCap; 3use ide_db::helpers::SnippetCap;
4use syntax::T;
4 5
5use crate::{ 6use crate::{
6 context::PathCompletionContext, item::Builder, CompletionContext, CompletionItem, 7 context::PathCompletionContext, item::Builder, CompletionContext, CompletionItem,
@@ -35,9 +36,13 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
35} 36}
36 37
37pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) { 38pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
38 if !ctx.expects_item() { 39 if !ctx.expects_item() || ctx.previous_token_is(T![unsafe]) || ctx.path_qual().is_some() {
39 return; 40 return;
40 } 41 }
42 if ctx.has_visibility_prev_sibling() {
43 return; // technically we could do some of these snippet completions if we were to put the
44 // attributes before the vis node.
45 }
41 let cap = match ctx.config.snippet_cap { 46 let cap = match ctx.config.snippet_cap {
42 Some(it) => it, 47 Some(it) => it,
43 None => return, 48 None => return,
@@ -82,10 +87,10 @@ fn ${1:feature}() {
82mod tests { 87mod tests {
83 use expect_test::{expect, Expect}; 88 use expect_test::{expect, Expect};
84 89
85 use crate::{test_utils::completion_list, CompletionKind}; 90 use crate::{tests::filtered_completion_list, CompletionKind};
86 91
87 fn check(ra_fixture: &str, expect: Expect) { 92 fn check(ra_fixture: &str, expect: Expect) {
88 let actual = completion_list(ra_fixture, CompletionKind::Snippet); 93 let actual = filtered_completion_list(ra_fixture, CompletionKind::Snippet);
89 expect.assert_eq(&actual) 94 expect.assert_eq(&actual)
90 } 95 }
91 96
@@ -105,21 +110,4 @@ mod tests {
105 check(r#"fn foo(x: i32) { ::foo$0 }"#, expect![[""]]); 110 check(r#"fn foo(x: i32) { ::foo$0 }"#, expect![[""]]);
106 check(r#"fn foo(x: i32) { ::$0 }"#, expect![[""]]); 111 check(r#"fn foo(x: i32) { ::$0 }"#, expect![[""]]);
107 } 112 }
108
109 #[test]
110 fn completes_snippets_in_items() {
111 check(
112 r#"
113#[cfg(test)]
114mod tests {
115 $0
116}
117"#,
118 expect![[r#"
119 sn tmod (Test module)
120 sn tfn (Test function)
121 sn macro_rules
122 "#]],
123 )
124 }
125} 113}
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index a60e5f43c..dc1d198cc 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -246,12 +246,12 @@ mod tests {
246 use expect_test::{expect, Expect}; 246 use expect_test::{expect, Expect};
247 247
248 use crate::{ 248 use crate::{
249 test_utils::{check_edit, completion_list}, 249 tests::{check_edit, filtered_completion_list},
250 CompletionKind, 250 CompletionKind,
251 }; 251 };
252 252
253 fn check(ra_fixture: &str, expect: Expect) { 253 fn check(ra_fixture: &str, expect: Expect) {
254 let actual = completion_list(ra_fixture, CompletionKind::Magic); 254 let actual = filtered_completion_list(ra_fixture, CompletionKind::Magic);
255 expect.assert_eq(&actual) 255 expect.assert_eq(&actual)
256 } 256 }
257 257
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index b5af1c810..2868d9b18 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -40,7 +40,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
40 if let Some(hir::Adt::Enum(e)) = 40 if let Some(hir::Adt::Enum(e)) =
41 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) 41 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
42 { 42 {
43 super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| { 43 super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
44 acc.add_qualified_enum_variant(ctx, variant, path) 44 acc.add_qualified_enum_variant(ctx, variant, path)
45 }); 45 });
46 } 46 }
@@ -93,7 +93,7 @@ mod tests {
93 use expect_test::{expect, Expect}; 93 use expect_test::{expect, Expect};
94 94
95 use crate::{ 95 use crate::{
96 test_utils::{check_edit, completion_list_with_config, TEST_CONFIG}, 96 tests::{check_edit, filtered_completion_list_with_config, TEST_CONFIG},
97 CompletionConfig, CompletionKind, 97 CompletionConfig, CompletionKind,
98 }; 98 };
99 99
@@ -102,7 +102,8 @@ mod tests {
102 } 102 }
103 103
104 fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) { 104 fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) {
105 let actual = completion_list_with_config(config, ra_fixture, CompletionKind::Reference); 105 let actual =
106 filtered_completion_list_with_config(config, ra_fixture, CompletionKind::Reference);
106 expect.assert_eq(&actual) 107 expect.assert_eq(&actual)
107 } 108 }
108 109
@@ -500,18 +501,6 @@ fn f() {$0}
500 check( 501 check(
501 r#" 502 r#"
502#[rustc_builtin_macro] 503#[rustc_builtin_macro]
503pub macro Clone {}
504
505struct S;
506impl S {
507 $0
508}
509"#,
510 expect![[r#""#]],
511 );
512 check(
513 r#"
514#[rustc_builtin_macro]
515pub macro bench {} 504pub macro bench {}
516 505
517fn f() {$0} 506fn f() {$0}
@@ -773,42 +762,6 @@ impl My$0
773 } 762 }
774 763
775 #[test] 764 #[test]
776 fn completes_in_assoc_item_list() {
777 check(
778 r#"
779macro_rules! foo {}
780mod bar {}
781
782struct MyStruct {}
783impl MyStruct {
784 $0
785}
786"#,
787 expect![[r#"
788 md bar
789 ma foo!(…) macro_rules! foo
790 "#]],
791 )
792 }
793
794 #[test]
795 fn completes_in_item_list() {
796 check(
797 r#"
798struct MyStruct {}
799macro_rules! foo {}
800mod bar {}
801
802$0
803"#,
804 expect![[r#"
805 md bar
806 ma foo!(…) macro_rules! foo
807 "#]],
808 )
809 }
810
811 #[test]
812 fn completes_types_and_const_in_arg_list() { 765 fn completes_types_and_const_in_arg_list() {
813 check( 766 check(
814 r#" 767 r#"
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index a8437d81c..121909857 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -302,18 +302,28 @@ impl<'a> CompletionContext<'a> {
302 ) 302 )
303 } 303 }
304 304
305 pub(crate) fn has_visibility_prev_sibling(&self) -> bool {
306 matches!(self.prev_sibling, Some(ImmediatePrevSibling::Visibility))
307 }
308
305 pub(crate) fn after_if(&self) -> bool { 309 pub(crate) fn after_if(&self) -> bool {
306 matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr)) 310 matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr))
307 } 311 }
308 312
309 pub(crate) fn is_path_disallowed(&self) -> bool { 313 pub(crate) fn is_path_disallowed(&self) -> bool {
310 matches!( 314 self.attribute_under_caret.is_some()
311 self.completion_location, 315 || self.previous_token_is(T![unsafe])
312 Some(ImmediateLocation::Attribute(_)) 316 || matches!(
313 | Some(ImmediateLocation::ModDeclaration(_)) 317 self.prev_sibling,
314 | Some(ImmediateLocation::RecordPat(_)) 318 Some(ImmediatePrevSibling::Attribute) | Some(ImmediatePrevSibling::Visibility)
315 | Some(ImmediateLocation::RecordExpr(_)) 319 )
316 ) || self.attribute_under_caret.is_some() 320 || matches!(
321 self.completion_location,
322 Some(ImmediateLocation::Attribute(_))
323 | Some(ImmediateLocation::ModDeclaration(_))
324 | Some(ImmediateLocation::RecordPat(_))
325 | Some(ImmediateLocation::RecordExpr(_))
326 )
317 } 327 }
318 328
319 pub(crate) fn expects_expression(&self) -> bool { 329 pub(crate) fn expects_expression(&self) -> bool {
@@ -685,7 +695,7 @@ mod tests {
685 use expect_test::{expect, Expect}; 695 use expect_test::{expect, Expect};
686 use hir::HirDisplay; 696 use hir::HirDisplay;
687 697
688 use crate::test_utils::{position, TEST_CONFIG}; 698 use crate::tests::{position, TEST_CONFIG};
689 699
690 use super::CompletionContext; 700 use super::CompletionContext;
691 701
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 18983aa01..bf73818dc 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -1,14 +1,14 @@
1//! `completions` crate provides utilities for generating completions of user input. 1//! `completions` crate provides utilities for generating completions of user input.
2 2
3mod completions;
3mod config; 4mod config;
4mod item;
5mod context; 5mod context;
6mod item;
6mod patterns; 7mod patterns;
7#[cfg(test)]
8mod test_utils;
9mod render; 8mod render;
10 9
11mod completions; 10#[cfg(test)]
11mod tests;
12 12
13use completions::flyimport::position_for_import; 13use completions::flyimport::position_for_import;
14use ide_db::{ 14use ide_db::{
@@ -141,6 +141,7 @@ pub fn completions(
141 let ctx = CompletionContext::new(db, position, config)?; 141 let ctx = CompletionContext::new(db, position, config)?;
142 142
143 if ctx.no_completion_required() { 143 if ctx.no_completion_required() {
144 cov_mark::hit!(no_completion_required);
144 // No work required here. 145 // No work required here.
145 return None; 146 return None;
146 } 147 }
@@ -200,117 +201,3 @@ pub fn resolve_completion_edits(
200 201
201 ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit]) 202 ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit])
202} 203}
203
204#[cfg(test)]
205mod tests {
206 use crate::test_utils::{self, TEST_CONFIG};
207
208 struct DetailAndDocumentation<'a> {
209 detail: &'a str,
210 documentation: &'a str,
211 }
212
213 fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
214 let (db, position) = test_utils::position(ra_fixture);
215 let config = TEST_CONFIG;
216 let completions: Vec<_> = crate::completions(&db, &config, position).unwrap().into();
217 for item in completions {
218 if item.detail() == Some(expected.detail) {
219 let opt = item.documentation();
220 let doc = opt.as_ref().map(|it| it.as_str());
221 assert_eq!(doc, Some(expected.documentation));
222 return;
223 }
224 }
225 panic!("completion detail not found: {}", expected.detail)
226 }
227
228 fn check_no_completion(ra_fixture: &str) {
229 let (db, position) = test_utils::position(ra_fixture);
230 let config = TEST_CONFIG;
231
232 let completions: Option<Vec<String>> = crate::completions(&db, &config, position)
233 .and_then(|completions| {
234 let completions: Vec<_> = completions.into();
235 if completions.is_empty() {
236 None
237 } else {
238 Some(completions)
239 }
240 })
241 .map(|completions| {
242 completions.into_iter().map(|completion| format!("{:?}", completion)).collect()
243 });
244
245 // `assert_eq` instead of `assert!(completions.is_none())` to get the list of completions if test will panic.
246 assert_eq!(completions, None, "Completions were generated, but weren't expected");
247 }
248
249 #[test]
250 fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
251 check_detail_and_documentation(
252 r#"
253macro_rules! bar {
254 () => {
255 struct Bar;
256 impl Bar {
257 #[doc = "Do the foo"]
258 fn foo(&self) {}
259 }
260 }
261}
262
263bar!();
264
265fn foo() {
266 let bar = Bar;
267 bar.fo$0;
268}
269"#,
270 DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" },
271 );
272 }
273
274 #[test]
275 fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
276 check_detail_and_documentation(
277 r#"
278macro_rules! bar {
279 () => {
280 struct Bar;
281 impl Bar {
282 /// Do the foo
283 fn foo(&self) {}
284 }
285 }
286}
287
288bar!();
289
290fn foo() {
291 let bar = Bar;
292 bar.fo$0;
293}
294"#,
295 DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" },
296 );
297 }
298
299 #[test]
300 fn test_no_completions_required() {
301 // There must be no hint for 'in' keyword.
302 check_no_completion(r#"fn foo() { for i i$0 }"#);
303 // After 'in' keyword hints may be spawned.
304 check_detail_and_documentation(
305 r#"
306/// Do the foo
307fn foo() -> &'static str { "foo" }
308
309fn bar() {
310 for c in fo$0
311}
312"#,
313 DetailAndDocumentation { detail: "fn() -> &str", documentation: "Do the foo" },
314 );
315 }
316}
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index 72e67e3c4..62e4334de 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -11,7 +11,7 @@ use syntax::{
11}; 11};
12 12
13#[cfg(test)] 13#[cfg(test)]
14use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; 14use crate::tests::{check_pattern_is_applicable, check_pattern_is_not_applicable};
15 15
16/// Immediate previous node to what we are completing. 16/// Immediate previous node to what we are completing.
17#[derive(Copy, Clone, Debug, PartialEq, Eq)] 17#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -19,6 +19,8 @@ pub(crate) enum ImmediatePrevSibling {
19 IfExpr, 19 IfExpr,
20 TraitDefName, 20 TraitDefName,
21 ImplDefType, 21 ImplDefType,
22 Visibility,
23 Attribute,
22} 24}
23 25
24/// Direct parent "thing" of what we are currently completing. 26/// Direct parent "thing" of what we are currently completing.
@@ -79,6 +81,17 @@ pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<Immedi
79 _ => node, 81 _ => node,
80 }; 82 };
81 let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; 83 let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
84 if prev_sibling.kind() == ERROR {
85 let prev_sibling = prev_sibling.first_child()?;
86 let res = match_ast! {
87 match prev_sibling {
88 // vis followed by random ident will always error the parser
89 ast::Visibility(_it) => ImmediatePrevSibling::Visibility,
90 _ => return None,
91 }
92 };
93 return Some(res);
94 }
82 let res = match_ast! { 95 let res = match_ast! {
83 match prev_sibling { 96 match prev_sibling {
84 ast::ExprStmt(it) => { 97 ast::ExprStmt(it) => {
@@ -101,6 +114,7 @@ pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<Immedi
101 } else { 114 } else {
102 return None 115 return None
103 }, 116 },
117 ast::Attr(_it) => ImmediatePrevSibling::Attribute,
104 _ => return None, 118 _ => return None,
105 } 119 }
106 }; 120 };
@@ -310,7 +324,7 @@ fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
310mod tests { 324mod tests {
311 use syntax::algo::find_node_at_offset; 325 use syntax::algo::find_node_at_offset;
312 326
313 use crate::test_utils::position; 327 use crate::tests::position;
314 328
315 use super::*; 329 use super::*;
316 330
@@ -421,4 +435,14 @@ mod tests {
421 check_prev_sibling(r"fn foo() { if true {} w$0", ImmediatePrevSibling::IfExpr); 435 check_prev_sibling(r"fn foo() { if true {} w$0", ImmediatePrevSibling::IfExpr);
422 check_prev_sibling(r"fn foo() { if true {}; w$0", None); 436 check_prev_sibling(r"fn foo() { if true {}; w$0", None);
423 } 437 }
438
439 #[test]
440 fn test_vis_prev_sibling() {
441 check_prev_sibling(r"pub w$0", ImmediatePrevSibling::Visibility);
442 }
443
444 #[test]
445 fn test_attr_prev_sibling() {
446 check_prev_sibling(r"#[attr] w$0", ImmediatePrevSibling::Attribute);
447 }
424} 448}
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 2bd2c44d0..4b55f7504 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -335,7 +335,7 @@ mod tests {
335 335
336 use crate::{ 336 use crate::{
337 item::CompletionRelevanceTypeMatch, 337 item::CompletionRelevanceTypeMatch,
338 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, 338 tests::{check_edit, do_completion, get_all_items, TEST_CONFIG},
339 CompletionKind, CompletionRelevance, 339 CompletionKind, CompletionRelevance,
340 }; 340 };
341 341
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
index 28f056e77..91dc178f3 100644
--- a/crates/ide_completion/src/render/enum_variant.rs
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -121,7 +121,7 @@ impl<'a> EnumRender<'a> {
121 121
122#[cfg(test)] 122#[cfg(test)]
123mod tests { 123mod tests {
124 use crate::test_utils::check_edit; 124 use crate::tests::check_edit;
125 125
126 #[test] 126 #[test]
127 fn inserts_parens_for_tuple_enums() { 127 fn inserts_parens_for_tuple_enums() {
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index 1357b9f4a..19f2c86e9 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -191,7 +191,7 @@ impl<'a> FunctionRender<'a> {
191#[cfg(test)] 191#[cfg(test)]
192mod tests { 192mod tests {
193 use crate::{ 193 use crate::{
194 test_utils::{check_edit, check_edit_with_config, TEST_CONFIG}, 194 tests::{check_edit, check_edit_with_config, TEST_CONFIG},
195 CompletionConfig, 195 CompletionConfig,
196 }; 196 };
197 197
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index 3a7238bb8..d5a1f45d3 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -133,7 +133,7 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
133 133
134#[cfg(test)] 134#[cfg(test)]
135mod tests { 135mod tests {
136 use crate::test_utils::check_edit; 136 use crate::tests::check_edit;
137 137
138 #[test] 138 #[test]
139 fn dont_insert_macro_call_parens_unncessary() { 139 fn dont_insert_macro_call_parens_unncessary() {
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/tests.rs
index b0a4b2026..1495924ea 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/tests.rs
@@ -1,4 +1,4 @@
1//! Runs completion for testing purposes. 1mod item_list;
2 2
3use hir::{PrefixKind, Semantics}; 3use hir::{PrefixKind, Semantics};
4use ide_db::{ 4use ide_db::{
@@ -31,6 +31,14 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
31 }, 31 },
32}; 32};
33 33
34fn completion_list(code: &str) -> String {
35 completion_list_with_config(TEST_CONFIG, code)
36}
37
38fn completion_list_with_config(config: CompletionConfig, code: &str) -> String {
39 render_completion_list(get_all_items(config, code))
40}
41
34/// Creates analysis from a multi-file fixture, returns positions marked with $0. 42/// Creates analysis from a multi-file fixture, returns positions marked with $0.
35pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { 43pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
36 let change_fixture = ChangeFixture::parse(ra_fixture); 44 let change_fixture = ChangeFixture::parse(ra_fixture);
@@ -57,24 +65,27 @@ pub(crate) fn do_completion_with_config(
57 .collect() 65 .collect()
58} 66}
59 67
60pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { 68pub(crate) fn filtered_completion_list(code: &str, kind: CompletionKind) -> String {
61 completion_list_with_config(TEST_CONFIG, code, kind) 69 filtered_completion_list_with_config(TEST_CONFIG, code, kind)
62} 70}
63 71
64pub(crate) fn completion_list_with_config( 72pub(crate) fn filtered_completion_list_with_config(
65 config: CompletionConfig, 73 config: CompletionConfig,
66 code: &str, 74 code: &str,
67 kind: CompletionKind, 75 kind: CompletionKind,
68) -> String { 76) -> String {
69 let kind_completions: Vec<CompletionItem> = 77 let kind_completions: Vec<CompletionItem> =
70 get_all_items(config, code).into_iter().filter(|c| c.completion_kind == kind).collect(); 78 get_all_items(config, code).into_iter().filter(|c| c.completion_kind == kind).collect();
71 let label_width = kind_completions 79 render_completion_list(kind_completions)
72 .iter() 80}
73 .map(|it| monospace_width(it.label())) 81
74 .max() 82fn render_completion_list(completions: Vec<CompletionItem>) -> String {
75 .unwrap_or_default() 83 fn monospace_width(s: &str) -> usize {
76 .min(16); 84 s.chars().count()
77 kind_completions 85 }
86 let label_width =
87 completions.iter().map(|it| monospace_width(it.label())).max().unwrap_or_default().min(16);
88 completions
78 .into_iter() 89 .into_iter()
79 .map(|it| { 90 .map(|it| {
80 let tag = it.kind().unwrap().tag(); 91 let tag = it.kind().unwrap().tag();
@@ -93,10 +104,6 @@ pub(crate) fn completion_list_with_config(
93 .collect() 104 .collect()
94} 105}
95 106
96fn monospace_width(s: &str) -> usize {
97 s.chars().count()
98}
99
100pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 107pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
101 check_edit_with_config(TEST_CONFIG, what, ra_fixture_before, ra_fixture_after) 108 check_edit_with_config(TEST_CONFIG, what, ra_fixture_before, ra_fixture_after)
102} 109}
@@ -152,3 +159,18 @@ pub(crate) fn get_all_items(config: CompletionConfig, code: &str) -> Vec<Complet
152 let (db, position) = position(code); 159 let (db, position) = position(code);
153 crate::completions(&db, &config, position).unwrap().into() 160 crate::completions(&db, &config, position).unwrap().into()
154} 161}
162
163fn check_no_completion(ra_fixture: &str) {
164 let (db, position) = position(ra_fixture);
165
166 assert!(
167 crate::completions(&db, &TEST_CONFIG, position).is_none(),
168 "Completions were generated, but weren't expected"
169 );
170}
171
172#[test]
173fn test_no_completions_required() {
174 cov_mark::check!(no_completion_required);
175 check_no_completion(r#"fn foo() { for i i$0 }"#);
176}
diff --git a/crates/ide_completion/src/tests/item_list.rs b/crates/ide_completion/src/tests/item_list.rs
new file mode 100644
index 000000000..7c124ac37
--- /dev/null
+++ b/crates/ide_completion/src/tests/item_list.rs
@@ -0,0 +1,223 @@
1use expect_test::{expect, Expect};
2
3use crate::tests::completion_list;
4
5fn check(ra_fixture: &str, expect: Expect) {
6 let base = r#"#[rustc_builtin_macro]
7pub macro Clone {}
8enum Enum { Variant }
9struct Struct {}
10#[macro_export]
11macro_rules! foo {}
12mod bar {}
13const CONST: () = ();
14trait Trait {}
15"#;
16 let actual = completion_list(&format!("{}{}", base, ra_fixture));
17 expect.assert_eq(&actual)
18}
19
20#[test]
21fn in_mod_item_list() {
22 check(
23 r#"mod tests { $0 }"#,
24 expect![[r##"
25 kw pub(crate)
26 kw pub
27 kw unsafe
28 kw fn
29 kw const
30 kw type
31 kw impl
32 kw extern
33 kw use
34 kw trait
35 kw static
36 kw mod
37 kw enum
38 kw struct
39 kw union
40 sn tmod (Test module)
41 sn tfn (Test function)
42 sn macro_rules
43 ma foo!(…) #[macro_export] macro_rules! foo
44 "##]],
45 )
46}
47
48#[test]
49fn in_source_file_item_list() {
50 check(
51 r#"$0"#,
52 expect![[r##"
53 kw pub(crate)
54 kw pub
55 kw unsafe
56 kw fn
57 kw const
58 kw type
59 kw impl
60 kw extern
61 kw use
62 kw trait
63 kw static
64 kw mod
65 kw enum
66 kw struct
67 kw union
68 sn tmod (Test module)
69 sn tfn (Test function)
70 sn macro_rules
71 md bar
72 ma foo!(…) #[macro_export] macro_rules! foo
73 ma foo!(…) #[macro_export] macro_rules! foo
74 "##]],
75 )
76}
77
78#[test]
79fn in_item_list_after_attr() {
80 check(
81 r#"#[attr] $0"#,
82 expect![[r#"
83 kw pub(crate)
84 kw pub
85 kw unsafe
86 kw fn
87 kw const
88 kw type
89 kw impl
90 kw extern
91 kw use
92 kw trait
93 kw static
94 kw mod
95 kw enum
96 kw struct
97 kw union
98 sn tmod (Test module)
99 sn tfn (Test function)
100 sn macro_rules
101 "#]],
102 )
103}
104
105#[test]
106fn in_qualified_path() {
107 check(
108 r#"crate::$0"#,
109 expect![[r##"
110 kw pub(crate)
111 kw pub
112 kw unsafe
113 kw fn
114 kw const
115 kw type
116 kw impl
117 kw extern
118 kw use
119 kw trait
120 kw static
121 kw mod
122 kw enum
123 kw struct
124 kw union
125 md bar
126 ma foo!(…) #[macro_export] macro_rules! foo
127 "##]],
128 )
129}
130
131#[test]
132fn after_unsafe_token() {
133 check(
134 r#"unsafe $0"#,
135 expect![[r#"
136 kw fn
137 kw trait
138 kw impl
139 "#]],
140 );
141}
142
143#[test]
144fn after_visibility() {
145 check(
146 r#"pub $0"#,
147 expect![[r#"
148 kw unsafe
149 kw fn
150 kw const
151 kw type
152 kw use
153 kw trait
154 kw static
155 kw mod
156 kw enum
157 kw struct
158 kw union
159 "#]],
160 );
161}
162
163#[test]
164fn after_visibility_unsafe() {
165 // FIXME this shouldn't show `impl`
166 check(
167 r#"pub unsafe $0"#,
168 expect![[r#"
169 kw fn
170 kw trait
171 kw impl
172 "#]],
173 );
174}
175
176#[test]
177fn in_impl_assoc_item_list() {
178 check(
179 r#"impl Struct { $0 }"#,
180 expect![[r##"
181 kw pub(crate)
182 kw pub
183 kw unsafe
184 kw fn
185 kw const
186 kw type
187 md bar
188 ma foo!(…) #[macro_export] macro_rules! foo
189 ma foo!(…) #[macro_export] macro_rules! foo
190 "##]],
191 )
192}
193
194#[test]
195fn in_impl_assoc_item_list_after_attr() {
196 check(
197 r#"impl Struct { #[attr] $0 }"#,
198 expect![[r#"
199 kw pub(crate)
200 kw pub
201 kw unsafe
202 kw fn
203 kw const
204 kw type
205 "#]],
206 )
207}
208
209#[test]
210fn in_trait_assoc_item_list() {
211 check(
212 r"trait Foo { $0 }",
213 expect![[r##"
214 kw unsafe
215 kw fn
216 kw const
217 kw type
218 md bar
219 ma foo!(…) #[macro_export] macro_rules! foo
220 ma foo!(…) #[macro_export] macro_rules! foo
221 "##]],
222 );
223}