aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/completion')
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs300
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs38
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs295
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs46
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs75
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs41
-rw-r--r--crates/ra_ide/src/completion/presentation.rs104
7 files changed, 801 insertions, 98 deletions
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
index 8bf952798..f17266221 100644
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -3,25 +3,29 @@
3//! This module uses a bit of static metadata to provide completions 3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes. 4//! for built-in attributes.
5 5
6use super::completion_context::CompletionContext; 6use ra_syntax::{ast, AstNode, SyntaxKind};
7use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions}; 7use rustc_hash::FxHashSet;
8use ra_syntax::{ 8
9 ast::{Attr, AttrKind}, 9use crate::completion::{
10 AstNode, 10 completion_context::CompletionContext,
11 completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions},
11}; 12};
12 13
13pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) { 14pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
14 if !ctx.is_attribute { 15 let attribute = ctx.attribute_under_caret.as_ref()?;
15 return;
16 }
17 16
18 let is_inner = ctx 17 match (attribute.path(), attribute.input()) {
19 .original_token 18 (Some(path), Some(ast::AttrInput::TokenTree(token_tree)))
20 .ancestors() 19 if path.to_string() == "derive" =>
21 .find_map(Attr::cast) 20 {
22 .map(|attr| attr.kind() == AttrKind::Inner) 21 complete_derive(acc, ctx, token_tree)
23 .unwrap_or(false); 22 }
23 _ => complete_attribute_start(acc, ctx, attribute),
24 }
25 Some(())
26}
24 27
28fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
25 for attr_completion in ATTRIBUTES { 29 for attr_completion in ATTRIBUTES {
26 let mut item = CompletionItem::new( 30 let mut item = CompletionItem::new(
27 CompletionKind::Attribute, 31 CompletionKind::Attribute,
@@ -37,7 +41,7 @@ pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
37 _ => {} 41 _ => {}
38 } 42 }
39 43
40 if is_inner || !attr_completion.should_be_inner { 44 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.should_be_inner {
41 acc.add(item); 45 acc.add(item);
42 } 46 }
43 } 47 }
@@ -126,6 +130,106 @@ const ATTRIBUTES: &[AttrCompletion] = &[
126 }, 130 },
127]; 131];
128 132
133fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
134 if let Ok(existing_derives) = parse_derive_input(derive_input) {
135 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
136 .into_iter()
137 .filter(|completion| !existing_derives.contains(completion.label))
138 {
139 let mut label = derive_completion.label.to_owned();
140 for dependency in derive_completion
141 .dependencies
142 .into_iter()
143 .filter(|&&dependency| !existing_derives.contains(dependency))
144 {
145 label.push_str(", ");
146 label.push_str(dependency);
147 }
148 acc.add(
149 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
150 .kind(CompletionItemKind::Attribute),
151 );
152 }
153
154 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
155 acc.add(
156 CompletionItem::new(
157 CompletionKind::Attribute,
158 ctx.source_range(),
159 custom_derive_name,
160 )
161 .kind(CompletionItemKind::Attribute),
162 );
163 }
164 }
165}
166
167fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
168 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
169 (Some(left_paren), Some(right_paren))
170 if left_paren.kind() == SyntaxKind::L_PAREN
171 && right_paren.kind() == SyntaxKind::R_PAREN =>
172 {
173 let mut input_derives = FxHashSet::default();
174 let mut current_derive = String::new();
175 for token in derive_input
176 .syntax()
177 .children_with_tokens()
178 .filter_map(|token| token.into_token())
179 .skip_while(|token| token != &left_paren)
180 .skip(1)
181 .take_while(|token| token != &right_paren)
182 {
183 if SyntaxKind::COMMA == token.kind() {
184 if !current_derive.is_empty() {
185 input_derives.insert(current_derive);
186 current_derive = String::new();
187 }
188 } else {
189 current_derive.push_str(token.to_string().trim());
190 }
191 }
192
193 if !current_derive.is_empty() {
194 input_derives.insert(current_derive);
195 }
196 Ok(input_derives)
197 }
198 _ => Err(()),
199 }
200}
201
202fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
203 let mut result = FxHashSet::default();
204 ctx.scope().process_all_names(&mut |name, scope_def| {
205 if let hir::ScopeDef::MacroDef(mac) = scope_def {
206 if mac.is_derive_macro() {
207 result.insert(name.to_string());
208 }
209 }
210 });
211 result
212}
213
214struct DeriveCompletion {
215 label: &'static str,
216 dependencies: &'static [&'static str],
217}
218
219/// Standard Rust derives and the information about their dependencies
220/// (the dependencies are needed so that the main derive don't break the compilation when added)
221const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
222 DeriveCompletion { label: "Clone", dependencies: &[] },
223 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
224 DeriveCompletion { label: "Debug", dependencies: &[] },
225 DeriveCompletion { label: "Default", dependencies: &[] },
226 DeriveCompletion { label: "Hash", dependencies: &[] },
227 DeriveCompletion { label: "PartialEq", dependencies: &[] },
228 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
229 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
230 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
231];
232
129#[cfg(test)] 233#[cfg(test)]
130mod tests { 234mod tests {
131 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 235 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
@@ -136,6 +240,170 @@ mod tests {
136 } 240 }
137 241
138 #[test] 242 #[test]
243 fn empty_derive_completion() {
244 assert_debug_snapshot!(
245 do_attr_completion(
246 r"
247 #[derive(<|>)]
248 struct Test {}
249 ",
250 ),
251 @r###"
252 [
253 CompletionItem {
254 label: "Clone",
255 source_range: 30..30,
256 delete: 30..30,
257 insert: "Clone",
258 kind: Attribute,
259 },
260 CompletionItem {
261 label: "Copy, Clone",
262 source_range: 30..30,
263 delete: 30..30,
264 insert: "Copy, Clone",
265 kind: Attribute,
266 },
267 CompletionItem {
268 label: "Debug",
269 source_range: 30..30,
270 delete: 30..30,
271 insert: "Debug",
272 kind: Attribute,
273 },
274 CompletionItem {
275 label: "Default",
276 source_range: 30..30,
277 delete: 30..30,
278 insert: "Default",
279 kind: Attribute,
280 },
281 CompletionItem {
282 label: "Eq, PartialEq",
283 source_range: 30..30,
284 delete: 30..30,
285 insert: "Eq, PartialEq",
286 kind: Attribute,
287 },
288 CompletionItem {
289 label: "Hash",
290 source_range: 30..30,
291 delete: 30..30,
292 insert: "Hash",
293 kind: Attribute,
294 },
295 CompletionItem {
296 label: "Ord, PartialOrd, Eq, PartialEq",
297 source_range: 30..30,
298 delete: 30..30,
299 insert: "Ord, PartialOrd, Eq, PartialEq",
300 kind: Attribute,
301 },
302 CompletionItem {
303 label: "PartialEq",
304 source_range: 30..30,
305 delete: 30..30,
306 insert: "PartialEq",
307 kind: Attribute,
308 },
309 CompletionItem {
310 label: "PartialOrd, PartialEq",
311 source_range: 30..30,
312 delete: 30..30,
313 insert: "PartialOrd, PartialEq",
314 kind: Attribute,
315 },
316 ]
317 "###
318 );
319 }
320
321 #[test]
322 fn no_completion_for_incorrect_derive() {
323 assert_debug_snapshot!(
324 do_attr_completion(
325 r"
326 #[derive{<|>)]
327 struct Test {}
328 ",
329 ),
330 @"[]"
331 );
332 }
333
334 #[test]
335 fn derive_with_input_completion() {
336 assert_debug_snapshot!(
337 do_attr_completion(
338 r"
339 #[derive(serde::Serialize, PartialEq, <|>)]
340 struct Test {}
341 ",
342 ),
343 @r###"
344 [
345 CompletionItem {
346 label: "Clone",
347 source_range: 59..59,
348 delete: 59..59,
349 insert: "Clone",
350 kind: Attribute,
351 },
352 CompletionItem {
353 label: "Copy, Clone",
354 source_range: 59..59,
355 delete: 59..59,
356 insert: "Copy, Clone",
357 kind: Attribute,
358 },
359 CompletionItem {
360 label: "Debug",
361 source_range: 59..59,
362 delete: 59..59,
363 insert: "Debug",
364 kind: Attribute,
365 },
366 CompletionItem {
367 label: "Default",
368 source_range: 59..59,
369 delete: 59..59,
370 insert: "Default",
371 kind: Attribute,
372 },
373 CompletionItem {
374 label: "Eq",
375 source_range: 59..59,
376 delete: 59..59,
377 insert: "Eq",
378 kind: Attribute,
379 },
380 CompletionItem {
381 label: "Hash",
382 source_range: 59..59,
383 delete: 59..59,
384 insert: "Hash",
385 kind: Attribute,
386 },
387 CompletionItem {
388 label: "Ord, PartialOrd, Eq",
389 source_range: 59..59,
390 delete: 59..59,
391 insert: "Ord, PartialOrd, Eq",
392 kind: Attribute,
393 },
394 CompletionItem {
395 label: "PartialOrd",
396 source_range: 59..59,
397 delete: 59..59,
398 insert: "PartialOrd",
399 kind: Attribute,
400 },
401 ]
402 "###
403 );
404 }
405
406 #[test]
139 fn test_attribute_completion() { 407 fn test_attribute_completion() {
140 assert_debug_snapshot!( 408 assert_debug_snapshot!(
141 do_attr_completion( 409 do_attr_completion(
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 814354ffa..05f825c6f 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -250,6 +250,44 @@ mod tests {
250 } 250 }
251 251
252 #[test] 252 #[test]
253 fn test_union_field_completion() {
254 assert_debug_snapshot!(
255 do_ref_completion(
256 r"
257 union Un {
258 field: u8,
259 other: u16,
260 }
261
262 fn foo(u: Un) {
263 u.<|>
264 }
265 ",
266 ),
267 @r###"
268 [
269 CompletionItem {
270 label: "field",
271 source_range: 140..140,
272 delete: 140..140,
273 insert: "field",
274 kind: Field,
275 detail: "u8",
276 },
277 CompletionItem {
278 label: "other",
279 source_range: 140..140,
280 delete: 140..140,
281 insert: "other",
282 kind: Field,
283 detail: "u16",
284 },
285 ]
286 "###
287 );
288 }
289
290 #[test]
253 fn test_method_completion() { 291 fn test_method_completion() {
254 assert_debug_snapshot!( 292 assert_debug_snapshot!(
255 do_ref_completion( 293 do_ref_completion(
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
index dd10f74e6..7fcd22525 100644
--- a/crates/ra_ide/src/completion/complete_qualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_qualified_path.rs
@@ -2,6 +2,7 @@
2 2
3use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; 3use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
4use ra_syntax::AstNode; 4use ra_syntax::AstNode;
5use rustc_hash::FxHashSet;
5use test_utils::tested_by; 6use test_utils::tested_by;
6 7
7use crate::completion::{CompletionContext, Completions}; 8use crate::completion::{CompletionContext, Completions};
@@ -9,15 +10,29 @@ use crate::completion::{CompletionContext, Completions};
9pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10 let path = match &ctx.path_prefix { 11 let path = match &ctx.path_prefix {
11 Some(path) => path.clone(), 12 Some(path) => path.clone(),
12 _ => return, 13 None => return,
13 }; 14 };
14 let def = match ctx.scope().resolve_hir_path(&path) { 15
15 Some(PathResolution::Def(def)) => def, 16 if ctx.attribute_under_caret.is_some() {
16 _ => return, 17 return;
18 }
19
20 let scope = ctx.scope();
21 let context_module = scope.module();
22
23 let res = match scope.resolve_hir_path(&path) {
24 Some(res) => res,
25 None => return,
17 }; 26 };
18 let context_module = ctx.scope().module(); 27
19 match def { 28 // Add associated types on type parameters and `Self`.
20 hir::ModuleDef::Module(module) => { 29 res.assoc_type_shorthand_candidates(ctx.db, |alias| {
30 acc.add_type_alias(ctx, alias);
31 None::<()>
32 });
33
34 match res {
35 PathResolution::Def(hir::ModuleDef::Module(module)) => {
21 let module_scope = module.scope(ctx.db, context_module); 36 let module_scope = module.scope(ctx.db, context_module);
22 for (name, def) in module_scope { 37 for (name, def) in module_scope {
23 if ctx.use_item_syntax.is_some() { 38 if ctx.use_item_syntax.is_some() {
@@ -35,7 +50,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
35 acc.add_resolution(ctx, name.to_string(), &def); 50 acc.add_resolution(ctx, name.to_string(), &def);
36 } 51 }
37 } 52 }
38 hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => { 53 PathResolution::Def(def @ hir::ModuleDef::Adt(_))
54 | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) => {
39 if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { 55 if let hir::ModuleDef::Adt(Adt::Enum(e)) = def {
40 for variant in e.variants(ctx.db) { 56 for variant in e.variants(ctx.db) {
41 acc.add_enum_variant(ctx, variant, None); 57 acc.add_enum_variant(ctx, variant, None);
@@ -46,8 +62,10 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
46 hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), 62 hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
47 _ => unreachable!(), 63 _ => unreachable!(),
48 }; 64 };
49 // Iterate assoc types separately 65
50 // FIXME: complete T::AssocType 66 // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
67 // (where AssocType is defined on a trait, not an inherent impl)
68
51 let krate = ctx.krate; 69 let krate = ctx.krate;
52 if let Some(krate) = krate { 70 if let Some(krate) = krate {
53 let traits_in_scope = ctx.scope().traits_in_scope(); 71 let traits_in_scope = ctx.scope().traits_in_scope();
@@ -65,7 +83,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
65 None::<()> 83 None::<()>
66 }); 84 });
67 85
68 ty.iterate_impl_items(ctx.db, krate, |item| { 86 // Iterate assoc types separately
87 ty.iterate_assoc_items(ctx.db, krate, |item| {
69 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 88 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
70 return None; 89 return None;
71 } 90 }
@@ -77,7 +96,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
77 }); 96 });
78 } 97 }
79 } 98 }
80 hir::ModuleDef::Trait(t) => { 99 PathResolution::Def(hir::ModuleDef::Trait(t)) => {
100 // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
81 for item in t.items(ctx.db) { 101 for item in t.items(ctx.db) {
82 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 102 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
83 continue; 103 continue;
@@ -91,8 +111,38 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
91 } 111 }
92 } 112 }
93 } 113 }
114 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => {
115 if let Some(krate) = ctx.krate {
116 let ty = match res {
117 PathResolution::TypeParam(param) => param.ty(ctx.db),
118 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
119 _ => return,
120 };
121
122 let traits_in_scope = ctx.scope().traits_in_scope();
123 let mut seen = FxHashSet::default();
124 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
125 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
126 return None;
127 }
128
129 // We might iterate candidates of a trait multiple times here, so deduplicate
130 // them.
131 if seen.insert(item) {
132 match item {
133 hir::AssocItem::Function(func) => {
134 acc.add_function(ctx, func, None);
135 }
136 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
137 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
138 }
139 }
140 None::<()>
141 });
142 }
143 }
94 _ => {} 144 _ => {}
95 }; 145 }
96} 146}
97 147
98#[cfg(test)] 148#[cfg(test)]
@@ -844,6 +894,211 @@ mod tests {
844 } 894 }
845 895
846 #[test] 896 #[test]
897 fn completes_ty_param_assoc_ty() {
898 assert_debug_snapshot!(
899 do_reference_completion(
900 "
901 //- /lib.rs
902 trait Super {
903 type Ty;
904 const CONST: u8;
905 fn func() {}
906 fn method(&self) {}
907 }
908
909 trait Sub: Super {
910 type SubTy;
911 const C2: ();
912 fn subfunc() {}
913 fn submethod(&self) {}
914 }
915
916 fn foo<T: Sub>() {
917 T::<|>
918 }
919 "
920 ),
921 @r###"
922 [
923 CompletionItem {
924 label: "C2",
925 source_range: 219..219,
926 delete: 219..219,
927 insert: "C2",
928 kind: Const,
929 detail: "const C2: ();",
930 },
931 CompletionItem {
932 label: "CONST",
933 source_range: 219..219,
934 delete: 219..219,
935 insert: "CONST",
936 kind: Const,
937 detail: "const CONST: u8;",
938 },
939 CompletionItem {
940 label: "SubTy",
941 source_range: 219..219,
942 delete: 219..219,
943 insert: "SubTy",
944 kind: TypeAlias,
945 detail: "type SubTy;",
946 },
947 CompletionItem {
948 label: "Ty",
949 source_range: 219..219,
950 delete: 219..219,
951 insert: "Ty",
952 kind: TypeAlias,
953 detail: "type Ty;",
954 },
955 CompletionItem {
956 label: "func()",
957 source_range: 219..219,
958 delete: 219..219,
959 insert: "func()$0",
960 kind: Function,
961 lookup: "func",
962 detail: "fn func()",
963 },
964 CompletionItem {
965 label: "method()",
966 source_range: 219..219,
967 delete: 219..219,
968 insert: "method()$0",
969 kind: Method,
970 lookup: "method",
971 detail: "fn method(&self)",
972 },
973 CompletionItem {
974 label: "subfunc()",
975 source_range: 219..219,
976 delete: 219..219,
977 insert: "subfunc()$0",
978 kind: Function,
979 lookup: "subfunc",
980 detail: "fn subfunc()",
981 },
982 CompletionItem {
983 label: "submethod()",
984 source_range: 219..219,
985 delete: 219..219,
986 insert: "submethod()$0",
987 kind: Method,
988 lookup: "submethod",
989 detail: "fn submethod(&self)",
990 },
991 ]
992 "###
993 );
994 }
995
996 #[test]
997 fn completes_self_param_assoc_ty() {
998 assert_debug_snapshot!(
999 do_reference_completion(
1000 "
1001 //- /lib.rs
1002 trait Super {
1003 type Ty;
1004 const CONST: u8 = 0;
1005 fn func() {}
1006 fn method(&self) {}
1007 }
1008
1009 trait Sub: Super {
1010 type SubTy;
1011 const C2: () = ();
1012 fn subfunc() {}
1013 fn submethod(&self) {}
1014 }
1015
1016 struct Wrap<T>(T);
1017 impl<T> Super for Wrap<T> {}
1018 impl<T> Sub for Wrap<T> {
1019 fn subfunc() {
1020 // Should be able to assume `Self: Sub + Super`
1021 Self::<|>
1022 }
1023 }
1024 "
1025 ),
1026 @r###"
1027 [
1028 CompletionItem {
1029 label: "C2",
1030 source_range: 365..365,
1031 delete: 365..365,
1032 insert: "C2",
1033 kind: Const,
1034 detail: "const C2: () = ();",
1035 },
1036 CompletionItem {
1037 label: "CONST",
1038 source_range: 365..365,
1039 delete: 365..365,
1040 insert: "CONST",
1041 kind: Const,
1042 detail: "const CONST: u8 = 0;",
1043 },
1044 CompletionItem {
1045 label: "SubTy",
1046 source_range: 365..365,
1047 delete: 365..365,
1048 insert: "SubTy",
1049 kind: TypeAlias,
1050 detail: "type SubTy;",
1051 },
1052 CompletionItem {
1053 label: "Ty",
1054 source_range: 365..365,
1055 delete: 365..365,
1056 insert: "Ty",
1057 kind: TypeAlias,
1058 detail: "type Ty;",
1059 },
1060 CompletionItem {
1061 label: "func()",
1062 source_range: 365..365,
1063 delete: 365..365,
1064 insert: "func()$0",
1065 kind: Function,
1066 lookup: "func",
1067 detail: "fn func()",
1068 },
1069 CompletionItem {
1070 label: "method()",
1071 source_range: 365..365,
1072 delete: 365..365,
1073 insert: "method()$0",
1074 kind: Method,
1075 lookup: "method",
1076 detail: "fn method(&self)",
1077 },
1078 CompletionItem {
1079 label: "subfunc()",
1080 source_range: 365..365,
1081 delete: 365..365,
1082 insert: "subfunc()$0",
1083 kind: Function,
1084 lookup: "subfunc",
1085 detail: "fn subfunc()",
1086 },
1087 CompletionItem {
1088 label: "submethod()",
1089 source_range: 365..365,
1090 delete: 365..365,
1091 insert: "submethod()$0",
1092 kind: Method,
1093 lookup: "submethod",
1094 detail: "fn submethod(&self)",
1095 },
1096 ]
1097 "###
1098 );
1099 }
1100
1101 #[test]
847 fn completes_type_alias() { 1102 fn completes_type_alias() {
848 assert_debug_snapshot!( 1103 assert_debug_snapshot!(
849 do_reference_completion( 1104 do_reference_completion(
@@ -1075,4 +1330,18 @@ mod tests {
1075 "### 1330 "###
1076 ); 1331 );
1077 } 1332 }
1333
1334 #[test]
1335 fn dont_complete_attr() {
1336 assert_debug_snapshot!(
1337 do_reference_completion(
1338 r"
1339 mod foo { pub struct Foo; }
1340 #[foo::<|>]
1341 fn f() {}
1342 "
1343 ),
1344 @r###"[]"###
1345 )
1346 }
1078} 1347}
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index ee32d1ff6..039df03e0 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -32,7 +32,7 @@
32//! ``` 32//! ```
33 33
34use hir::{self, Docs, HasSource}; 34use hir::{self, Docs, HasSource};
35use ra_assists::utils::get_missing_impl_items; 35use ra_assists::utils::get_missing_assoc_items;
36use ra_syntax::{ 36use ra_syntax::{
37 ast::{self, edit, ImplDef}, 37 ast::{self, edit, ImplDef},
38 AstNode, SyntaxKind, SyntaxNode, TextRange, T, 38 AstNode, SyntaxKind, SyntaxNode, TextRange, T,
@@ -50,7 +50,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
50 if let Some((trigger, impl_def)) = completion_match(ctx) { 50 if let Some((trigger, impl_def)) = completion_match(ctx) {
51 match trigger.kind() { 51 match trigger.kind() {
52 SyntaxKind::NAME_REF => { 52 SyntaxKind::NAME_REF => {
53 get_missing_impl_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { 53 get_missing_assoc_items(&ctx.sema, &impl_def).iter().for_each(|item| match item {
54 hir::AssocItem::Function(fn_item) => { 54 hir::AssocItem::Function(fn_item) => {
55 add_function_impl(&trigger, acc, ctx, &fn_item) 55 add_function_impl(&trigger, acc, ctx, &fn_item)
56 } 56 }
@@ -64,34 +64,40 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
64 } 64 }
65 65
66 SyntaxKind::FN_DEF => { 66 SyntaxKind::FN_DEF => {
67 for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( 67 for missing_fn in
68 |item| match item { 68 get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| {
69 hir::AssocItem::Function(fn_item) => Some(fn_item), 69 match item {
70 _ => None, 70 hir::AssocItem::Function(fn_item) => Some(fn_item),
71 }, 71 _ => None,
72 ) { 72 }
73 })
74 {
73 add_function_impl(&trigger, acc, ctx, &missing_fn); 75 add_function_impl(&trigger, acc, ctx, &missing_fn);
74 } 76 }
75 } 77 }
76 78
77 SyntaxKind::TYPE_ALIAS_DEF => { 79 SyntaxKind::TYPE_ALIAS_DEF => {
78 for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( 80 for missing_fn in
79 |item| match item { 81 get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| {
80 hir::AssocItem::TypeAlias(type_item) => Some(type_item), 82 match item {
81 _ => None, 83 hir::AssocItem::TypeAlias(type_item) => Some(type_item),
82 }, 84 _ => None,
83 ) { 85 }
86 })
87 {
84 add_type_alias_impl(&trigger, acc, ctx, &missing_fn); 88 add_type_alias_impl(&trigger, acc, ctx, &missing_fn);
85 } 89 }
86 } 90 }
87 91
88 SyntaxKind::CONST_DEF => { 92 SyntaxKind::CONST_DEF => {
89 for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( 93 for missing_fn in
90 |item| match item { 94 get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| {
91 hir::AssocItem::Const(const_item) => Some(const_item), 95 match item {
92 _ => None, 96 hir::AssocItem::Const(const_item) => Some(const_item),
93 }, 97 _ => None,
94 ) { 98 }
99 })
100 {
95 add_const_impl(&trigger, acc, ctx, &missing_fn); 101 add_const_impl(&trigger, acc, ctx, &missing_fn);
96 } 102 }
97 } 103 }
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index 56cd086c6..bd40af1cb 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -4,18 +4,23 @@ use hir::ScopeDef;
4use test_utils::tested_by; 4use test_utils::tested_by;
5 5
6use crate::completion::{CompletionContext, Completions}; 6use crate::completion::{CompletionContext, Completions};
7use hir::{Adt, ModuleDef}; 7use hir::{Adt, ModuleDef, Type};
8use ra_syntax::AstNode; 8use ra_syntax::AstNode;
9 9
10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
11 if (!ctx.is_trivial_path && !ctx.is_pat_binding_or_const) 11 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
12 || ctx.record_lit_syntax.is_some() 12 return;
13 }
14 if ctx.record_lit_syntax.is_some()
13 || ctx.record_pat_syntax.is_some() 15 || ctx.record_pat_syntax.is_some()
16 || ctx.attribute_under_caret.is_some()
14 { 17 {
15 return; 18 return;
16 } 19 }
17 20
18 complete_enum_variants(acc, ctx); 21 if let Some(ty) = &ctx.expected_type {
22 complete_enum_variants(acc, ctx, ty);
23 }
19 24
20 if ctx.is_pat_binding_or_const { 25 if ctx.is_pat_binding_or_const {
21 return; 26 return;
@@ -34,26 +39,24 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
34 }); 39 });
35} 40}
36 41
37fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext) { 42fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
38 if let Some(ty) = ctx.expected_type_of(&ctx.token.parent()) { 43 if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
39 if let Some(Adt::Enum(enum_data)) = ty.as_adt() { 44 let variants = enum_data.variants(ctx.db);
40 let variants = enum_data.variants(ctx.db); 45
41 46 let module = if let Some(module) = ctx.scope().module() {
42 let module = if let Some(module) = ctx.scope().module() { 47 // Compute path from the completion site if available.
43 // Compute path from the completion site if available. 48 module
44 module 49 } else {
45 } else { 50 // Otherwise fall back to the enum's definition site.
46 // Otherwise fall back to the enum's definition site. 51 enum_data.module(ctx.db)
47 enum_data.module(ctx.db) 52 };
48 }; 53
49 54 for variant in variants {
50 for variant in variants { 55 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
51 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { 56 // Variants with trivial paths are already added by the existing completion logic,
52 // Variants with trivial paths are already added by the existing completion logic, 57 // so we should avoid adding these twice
53 // so we should avoid adding these twice 58 if path.segments.len() > 1 {
54 if path.segments.len() > 1 { 59 acc.add_qualified_enum_variant(ctx, variant, path);
55 acc.add_enum_variant(ctx, variant, Some(path.to_string()));
56 }
57 } 60 }
58 } 61 }
59 } 62 }
@@ -1173,6 +1176,7 @@ mod tests {
1173 delete: 248..250, 1176 delete: 248..250,
1174 insert: "Foo::Bar", 1177 insert: "Foo::Bar",
1175 kind: EnumVariant, 1178 kind: EnumVariant,
1179 lookup: "Bar",
1176 detail: "()", 1180 detail: "()",
1177 }, 1181 },
1178 CompletionItem { 1182 CompletionItem {
@@ -1181,6 +1185,7 @@ mod tests {
1181 delete: 248..250, 1185 delete: 248..250,
1182 insert: "Foo::Baz", 1186 insert: "Foo::Baz",
1183 kind: EnumVariant, 1187 kind: EnumVariant,
1188 lookup: "Baz",
1184 detail: "()", 1189 detail: "()",
1185 }, 1190 },
1186 CompletionItem { 1191 CompletionItem {
@@ -1189,6 +1194,7 @@ mod tests {
1189 delete: 248..250, 1194 delete: 248..250,
1190 insert: "Foo::Quux", 1195 insert: "Foo::Quux",
1191 kind: EnumVariant, 1196 kind: EnumVariant,
1197 lookup: "Quux",
1192 detail: "()", 1198 detail: "()",
1193 }, 1199 },
1194 ] 1200 ]
@@ -1231,6 +1237,7 @@ mod tests {
1231 delete: 219..221, 1237 delete: 219..221,
1232 insert: "Foo::Bar", 1238 insert: "Foo::Bar",
1233 kind: EnumVariant, 1239 kind: EnumVariant,
1240 lookup: "Bar",
1234 detail: "()", 1241 detail: "()",
1235 }, 1242 },
1236 CompletionItem { 1243 CompletionItem {
@@ -1239,6 +1246,7 @@ mod tests {
1239 delete: 219..221, 1246 delete: 219..221,
1240 insert: "Foo::Baz", 1247 insert: "Foo::Baz",
1241 kind: EnumVariant, 1248 kind: EnumVariant,
1249 lookup: "Baz",
1242 detail: "()", 1250 detail: "()",
1243 }, 1251 },
1244 CompletionItem { 1252 CompletionItem {
@@ -1247,6 +1255,7 @@ mod tests {
1247 delete: 219..221, 1255 delete: 219..221,
1248 insert: "Foo::Quux", 1256 insert: "Foo::Quux",
1249 kind: EnumVariant, 1257 kind: EnumVariant,
1258 lookup: "Quux",
1250 detail: "()", 1259 detail: "()",
1251 }, 1260 },
1252 ] 1261 ]
@@ -1285,6 +1294,7 @@ mod tests {
1285 delete: 185..186, 1294 delete: 185..186,
1286 insert: "Foo::Bar", 1295 insert: "Foo::Bar",
1287 kind: EnumVariant, 1296 kind: EnumVariant,
1297 lookup: "Bar",
1288 detail: "()", 1298 detail: "()",
1289 }, 1299 },
1290 CompletionItem { 1300 CompletionItem {
@@ -1293,6 +1303,7 @@ mod tests {
1293 delete: 185..186, 1303 delete: 185..186,
1294 insert: "Foo::Baz", 1304 insert: "Foo::Baz",
1295 kind: EnumVariant, 1305 kind: EnumVariant,
1306 lookup: "Baz",
1296 detail: "()", 1307 detail: "()",
1297 }, 1308 },
1298 CompletionItem { 1309 CompletionItem {
@@ -1301,6 +1312,7 @@ mod tests {
1301 delete: 185..186, 1312 delete: 185..186,
1302 insert: "Foo::Quux", 1313 insert: "Foo::Quux",
1303 kind: EnumVariant, 1314 kind: EnumVariant,
1315 lookup: "Quux",
1304 detail: "()", 1316 detail: "()",
1305 }, 1317 },
1306 CompletionItem { 1318 CompletionItem {
@@ -1353,10 +1365,25 @@ mod tests {
1353 delete: 98..99, 1365 delete: 98..99,
1354 insert: "m::E::V", 1366 insert: "m::E::V",
1355 kind: EnumVariant, 1367 kind: EnumVariant,
1368 lookup: "V",
1356 detail: "()", 1369 detail: "()",
1357 }, 1370 },
1358 ] 1371 ]
1359 "### 1372 "###
1360 ) 1373 )
1361 } 1374 }
1375
1376 #[test]
1377 fn dont_complete_attr() {
1378 assert_debug_snapshot!(
1379 do_reference_completion(
1380 r"
1381 struct Foo;
1382 #[<|>]
1383 fn f() {}
1384 "
1385 ),
1386 @r###"[]"###
1387 )
1388 }
1362} 1389}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 5f2797e41..dd87bd119 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -5,7 +5,7 @@ use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::{find_covering_element, find_node_at_offset},
8 ast, AstNode, 8 ast, match_ast, AstNode,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize, 10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11}; 11};
@@ -26,6 +26,7 @@ pub(crate) struct CompletionContext<'a> {
26 /// The token before the cursor, in the macro-expanded file. 26 /// The token before the cursor, in the macro-expanded file.
27 pub(super) token: SyntaxToken, 27 pub(super) token: SyntaxToken,
28 pub(super) krate: Option<hir::Crate>, 28 pub(super) krate: Option<hir::Crate>,
29 pub(super) expected_type: Option<Type>,
29 pub(super) name_ref_syntax: Option<ast::NameRef>, 30 pub(super) name_ref_syntax: Option<ast::NameRef>,
30 pub(super) function_syntax: Option<ast::FnDef>, 31 pub(super) function_syntax: Option<ast::FnDef>,
31 pub(super) use_item_syntax: Option<ast::UseItem>, 32 pub(super) use_item_syntax: Option<ast::UseItem>,
@@ -57,7 +58,7 @@ pub(crate) struct CompletionContext<'a> {
57 pub(super) is_macro_call: bool, 58 pub(super) is_macro_call: bool,
58 pub(super) is_path_type: bool, 59 pub(super) is_path_type: bool,
59 pub(super) has_type_args: bool, 60 pub(super) has_type_args: bool,
60 pub(super) is_attribute: bool, 61 pub(super) attribute_under_caret: Option<ast::Attr>,
61} 62}
62 63
63impl<'a> CompletionContext<'a> { 64impl<'a> CompletionContext<'a> {
@@ -93,6 +94,7 @@ impl<'a> CompletionContext<'a> {
93 token, 94 token,
94 offset: position.offset, 95 offset: position.offset,
95 krate, 96 krate,
97 expected_type: None,
96 name_ref_syntax: None, 98 name_ref_syntax: None,
97 function_syntax: None, 99 function_syntax: None,
98 use_item_syntax: None, 100 use_item_syntax: None,
@@ -114,7 +116,7 @@ impl<'a> CompletionContext<'a> {
114 is_path_type: false, 116 is_path_type: false,
115 has_type_args: false, 117 has_type_args: false,
116 dot_receiver_is_ambiguous_float_literal: false, 118 dot_receiver_is_ambiguous_float_literal: false,
117 is_attribute: false, 119 attribute_under_caret: None,
118 }; 120 };
119 121
120 let mut original_file = original_file.syntax().clone(); 122 let mut original_file = original_file.syntax().clone();
@@ -175,23 +177,31 @@ impl<'a> CompletionContext<'a> {
175 self.sema.scope_at_offset(&self.token.parent(), self.offset) 177 self.sema.scope_at_offset(&self.token.parent(), self.offset)
176 } 178 }
177 179
178 pub(crate) fn expected_type_of(&self, node: &SyntaxNode) -> Option<Type> {
179 for ancestor in node.ancestors() {
180 if let Some(pat) = ast::Pat::cast(ancestor.clone()) {
181 return self.sema.type_of_pat(&pat);
182 } else if let Some(expr) = ast::Expr::cast(ancestor) {
183 return self.sema.type_of_expr(&expr);
184 }
185 }
186 None
187 }
188
189 fn fill( 180 fn fill(
190 &mut self, 181 &mut self,
191 original_file: &SyntaxNode, 182 original_file: &SyntaxNode,
192 file_with_fake_ident: SyntaxNode, 183 file_with_fake_ident: SyntaxNode,
193 offset: TextSize, 184 offset: TextSize,
194 ) { 185 ) {
186 // FIXME: this is wrong in at least two cases:
187 // * when there's no token `foo(<|>)`
188 // * when there is a token, but it happens to have type of it's own
189 self.expected_type = self
190 .token
191 .ancestors()
192 .find_map(|node| {
193 let ty = match_ast! {
194 match node {
195 ast::Pat(it) => self.sema.type_of_pat(&it),
196 ast::Expr(it) => self.sema.type_of_expr(&it),
197 _ => return None,
198 }
199 };
200 Some(ty)
201 })
202 .flatten();
203 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
204
195 // First, let's try to complete a reference to some declaration. 205 // First, let's try to complete a reference to some declaration.
196 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { 206 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
197 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. 207 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
@@ -309,7 +319,6 @@ impl<'a> CompletionContext<'a> {
309 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) 319 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
310 .is_some(); 320 .is_some();
311 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); 321 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
312 self.is_attribute = path.syntax().parent().and_then(ast::Attr::cast).is_some();
313 322
314 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 323 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
315 self.has_type_args = segment.type_arg_list().is_some(); 324 self.has_type_args = segment.type_arg_list().is_some();
@@ -335,7 +344,7 @@ impl<'a> CompletionContext<'a> {
335 stmt.syntax().text_range() == name_ref.syntax().text_range(), 344 stmt.syntax().text_range() == name_ref.syntax().text_range(),
336 ); 345 );
337 } 346 }
338 if let Some(block) = ast::Block::cast(node) { 347 if let Some(block) = ast::BlockExpr::cast(node) {
339 return Some( 348 return Some(
340 block.expr().map(|e| e.syntax().text_range()) 349 block.expr().map(|e| e.syntax().text_range())
341 == Some(name_ref.syntax().text_range()), 350 == Some(name_ref.syntax().text_range()),
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 7633cd7fd..2edb130cf 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -1,6 +1,6 @@
1//! This modules takes care of rendering various definitions as completion items. 1//! This modules takes care of rendering various definitions as completion items.
2 2
3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type}; 3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
4use ra_syntax::ast::NameOwner; 4use ra_syntax::ast::NameOwner;
5use stdx::SepBy; 5use stdx::SepBy;
6use test_utils::tested_by; 6use test_utils::tested_by;
@@ -246,14 +246,37 @@ impl Completions {
246 .add_to(self); 246 .add_to(self);
247 } 247 }
248 248
249 pub(crate) fn add_qualified_enum_variant(
250 &mut self,
251 ctx: &CompletionContext,
252 variant: hir::EnumVariant,
253 path: ModPath,
254 ) {
255 self.add_enum_variant_impl(ctx, variant, None, Some(path))
256 }
257
249 pub(crate) fn add_enum_variant( 258 pub(crate) fn add_enum_variant(
250 &mut self, 259 &mut self,
251 ctx: &CompletionContext, 260 ctx: &CompletionContext,
252 variant: hir::EnumVariant, 261 variant: hir::EnumVariant,
253 local_name: Option<String>, 262 local_name: Option<String>,
254 ) { 263 ) {
264 self.add_enum_variant_impl(ctx, variant, local_name, None)
265 }
266
267 fn add_enum_variant_impl(
268 &mut self,
269 ctx: &CompletionContext,
270 variant: hir::EnumVariant,
271 local_name: Option<String>,
272 path: Option<ModPath>,
273 ) {
255 let is_deprecated = is_deprecated(variant, ctx.db); 274 let is_deprecated = is_deprecated(variant, ctx.db);
256 let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string()); 275 let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string());
276 let qualified_name = match &path {
277 Some(it) => it.to_string(),
278 None => name.to_string(),
279 };
257 let detail_types = variant 280 let detail_types = variant
258 .fields(ctx.db) 281 .fields(ctx.db)
259 .into_iter() 282 .into_iter()
@@ -271,16 +294,23 @@ impl Completions {
271 .surround_with("{ ", " }") 294 .surround_with("{ ", " }")
272 .to_string(), 295 .to_string(),
273 }; 296 };
274 let mut res = 297 let mut res = CompletionItem::new(
275 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone()) 298 CompletionKind::Reference,
276 .kind(CompletionItemKind::EnumVariant) 299 ctx.source_range(),
277 .set_documentation(variant.docs(ctx.db)) 300 qualified_name.clone(),
278 .set_deprecated(is_deprecated) 301 )
279 .detail(detail); 302 .kind(CompletionItemKind::EnumVariant)
303 .set_documentation(variant.docs(ctx.db))
304 .set_deprecated(is_deprecated)
305 .detail(detail);
306
307 if path.is_some() {
308 res = res.lookup_by(name);
309 }
280 310
281 if variant_kind == StructKind::Tuple { 311 if variant_kind == StructKind::Tuple {
282 let params = Params::Anonymous(variant.fields(ctx.db).len()); 312 let params = Params::Anonymous(variant.fields(ctx.db).len());
283 res = res.add_call_parens(ctx, name, params) 313 res = res.add_call_parens(ctx, qualified_name, params)
284 } 314 }
285 315
286 res.add_to(self); 316 res.add_to(self);
@@ -349,6 +379,14 @@ impl Builder {
349 if ctx.use_item_syntax.is_some() || ctx.is_call { 379 if ctx.use_item_syntax.is_some() || ctx.is_call {
350 return self; 380 return self;
351 } 381 }
382
383 // Don't add parentheses if the expected type is some function reference.
384 if let Some(ty) = &ctx.expected_type {
385 if ty.is_fn() {
386 return self;
387 }
388 }
389
352 let cap = match ctx.config.snippet_cap { 390 let cap = match ctx.config.snippet_cap {
353 Some(it) => it, 391 Some(it) => it,
354 None => return self, 392 None => return self,
@@ -749,6 +787,54 @@ mod tests {
749 } 787 }
750 788
751 #[test] 789 #[test]
790 fn no_call_parens_if_fn_ptr_needed() {
791 assert_debug_snapshot!(
792 do_reference_completion(
793 r"
794 fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8) {}
795
796 struct ManualVtable {
797 method: fn(u8, u8, u8, u8, u8),
798 }
799
800 fn main() -> ManualVtable {
801 ManualVtable {
802 method: some<|>
803 }
804 }
805 "
806 ),
807 @r###"
808 [
809 CompletionItem {
810 label: "ManualVtable",
811 source_range: 295..299,
812 delete: 295..299,
813 insert: "ManualVtable",
814 kind: Struct,
815 },
816 CompletionItem {
817 label: "main",
818 source_range: 295..299,
819 delete: 295..299,
820 insert: "main",
821 kind: Function,
822 detail: "fn main() -> ManualVtable",
823 },
824 CompletionItem {
825 label: "somefn",
826 source_range: 295..299,
827 delete: 295..299,
828 insert: "somefn",
829 kind: Function,
830 detail: "fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8)",
831 },
832 ]
833 "###
834 );
835 }
836
837 #[test]
752 fn arg_snippets_for_method_call() { 838 fn arg_snippets_for_method_call() {
753 assert_debug_snapshot!( 839 assert_debug_snapshot!(
754 do_reference_completion( 840 do_reference_completion(
@@ -1179,7 +1265,7 @@ mod tests {
1179 1265
1180 #[test] 1266 #[test]
1181 fn test_struct_field_completion_in_record_lit() { 1267 fn test_struct_field_completion_in_record_lit() {
1182 covers!(test_struct_field_completion_in_func_call); 1268 covers!(test_struct_field_completion_in_record_lit);
1183 assert_debug_snapshot!( 1269 assert_debug_snapshot!(
1184 do_reference_completion( 1270 do_reference_completion(
1185 r" 1271 r"