diff options
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r-- | crates/ide_completion/src/context.rs | 116 | ||||
-rw-r--r-- | crates/ide_completion/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ide_completion/src/render.rs | 7 |
3 files changed, 100 insertions, 27 deletions
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 787eb2fd3..2f3fb1710 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -196,46 +196,46 @@ impl<'a> CompletionContext<'a> { | |||
196 | }; | 196 | }; |
197 | 197 | ||
198 | let mut original_file = original_file.syntax().clone(); | 198 | let mut original_file = original_file.syntax().clone(); |
199 | let mut hypothetical_file = file_with_fake_ident.syntax().clone(); | 199 | let mut speculative_file = file_with_fake_ident.syntax().clone(); |
200 | let mut offset = position.offset; | 200 | let mut offset = position.offset; |
201 | let mut fake_ident_token = fake_ident_token; | 201 | let mut fake_ident_token = fake_ident_token; |
202 | 202 | ||
203 | // Are we inside a macro call? | 203 | // Are we inside a macro call? |
204 | while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = ( | 204 | while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = ( |
205 | find_node_at_offset::<ast::MacroCall>(&original_file, offset), | 205 | find_node_at_offset::<ast::MacroCall>(&original_file, offset), |
206 | find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset), | 206 | find_node_at_offset::<ast::MacroCall>(&speculative_file, offset), |
207 | ) { | 207 | ) { |
208 | if actual_macro_call.path().as_ref().map(|s| s.syntax().text()) | 208 | if actual_macro_call.path().as_ref().map(|s| s.syntax().text()) |
209 | != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text()) | 209 | != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text()) |
210 | { | 210 | { |
211 | break; | 211 | break; |
212 | } | 212 | } |
213 | let hypothetical_args = match macro_call_with_fake_ident.token_tree() { | 213 | let speculative_args = match macro_call_with_fake_ident.token_tree() { |
214 | Some(tt) => tt, | 214 | Some(tt) => tt, |
215 | None => break, | 215 | None => break, |
216 | }; | 216 | }; |
217 | if let (Some(actual_expansion), Some(hypothetical_expansion)) = ( | 217 | if let (Some(actual_expansion), Some(speculative_expansion)) = ( |
218 | ctx.sema.expand(&actual_macro_call), | 218 | ctx.sema.expand(&actual_macro_call), |
219 | ctx.sema.speculative_expand( | 219 | ctx.sema.speculative_expand( |
220 | &actual_macro_call, | 220 | &actual_macro_call, |
221 | &hypothetical_args, | 221 | &speculative_args, |
222 | fake_ident_token, | 222 | fake_ident_token, |
223 | ), | 223 | ), |
224 | ) { | 224 | ) { |
225 | let new_offset = hypothetical_expansion.1.text_range().start(); | 225 | let new_offset = speculative_expansion.1.text_range().start(); |
226 | if new_offset > actual_expansion.text_range().end() { | 226 | if new_offset > actual_expansion.text_range().end() { |
227 | break; | 227 | break; |
228 | } | 228 | } |
229 | original_file = actual_expansion; | 229 | original_file = actual_expansion; |
230 | hypothetical_file = hypothetical_expansion.0; | 230 | speculative_file = speculative_expansion.0; |
231 | fake_ident_token = hypothetical_expansion.1; | 231 | fake_ident_token = speculative_expansion.1; |
232 | offset = new_offset; | 232 | offset = new_offset; |
233 | } else { | 233 | } else { |
234 | break; | 234 | break; |
235 | } | 235 | } |
236 | } | 236 | } |
237 | ctx.fill_keyword_patterns(&hypothetical_file, offset); | 237 | ctx.fill_keyword_patterns(&speculative_file, offset); |
238 | ctx.fill(&original_file, hypothetical_file, offset); | 238 | ctx.fill(&original_file, speculative_file, offset); |
239 | Some(ctx) | 239 | Some(ctx) |
240 | } | 240 | } |
241 | 241 | ||
@@ -337,25 +337,24 @@ impl<'a> CompletionContext<'a> { | |||
337 | }, | 337 | }, |
338 | ast::RecordExprFieldList(_it) => { | 338 | ast::RecordExprFieldList(_it) => { |
339 | cov_mark::hit!(expected_type_struct_field_without_leading_char); | 339 | cov_mark::hit!(expected_type_struct_field_without_leading_char); |
340 | self.token.prev_sibling_or_token() | 340 | // wouldn't try {} be nice... |
341 | .and_then(|se| se.into_node()) | 341 | (|| { |
342 | .and_then(|node| ast::RecordExprField::cast(node)) | 342 | let expr_field = self.token.prev_sibling_or_token()? |
343 | .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf))) | 343 | .into_node() |
344 | .map(|(f, rf)|( | 344 | .and_then(|node| ast::RecordExprField::cast(node))?; |
345 | Some(f.0.ty(self.db)), | 345 | let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?; |
346 | rf.field_name().map(NameOrNameRef::NameRef), | 346 | Some(( |
347 | Some(ty), | ||
348 | expr_field.field_name().map(NameOrNameRef::NameRef), | ||
347 | )) | 349 | )) |
348 | .unwrap_or((None, None)) | 350 | })().unwrap_or((None, None)) |
349 | }, | 351 | }, |
350 | ast::RecordExprField(it) => { | 352 | ast::RecordExprField(it) => { |
351 | cov_mark::hit!(expected_type_struct_field_with_leading_char); | 353 | cov_mark::hit!(expected_type_struct_field_with_leading_char); |
352 | self.sema | 354 | ( |
353 | .resolve_record_field(&it) | 355 | it.expr().as_ref().and_then(|e| self.sema.type_of_expr(e)), |
354 | .map(|f|( | 356 | it.field_name().map(NameOrNameRef::NameRef), |
355 | Some(f.0.ty(self.db)), | 357 | ) |
356 | it.field_name().map(NameOrNameRef::NameRef), | ||
357 | )) | ||
358 | .unwrap_or((None, None)) | ||
359 | }, | 358 | }, |
360 | ast::MatchExpr(it) => { | 359 | ast::MatchExpr(it) => { |
361 | cov_mark::hit!(expected_type_match_arm_without_leading_char); | 360 | cov_mark::hit!(expected_type_match_arm_without_leading_char); |
@@ -382,6 +381,12 @@ impl<'a> CompletionContext<'a> { | |||
382 | let def = self.sema.to_def(&it); | 381 | let def = self.sema.to_def(&it); |
383 | (def.map(|def| def.ret_type(self.db)), None) | 382 | (def.map(|def| def.ret_type(self.db)), None) |
384 | }, | 383 | }, |
384 | ast::ClosureExpr(it) => { | ||
385 | let ty = self.sema.type_of_expr(&it.into()); | ||
386 | ty.and_then(|ty| ty.as_callable(self.db)) | ||
387 | .map(|c| (Some(c.return_type()), None)) | ||
388 | .unwrap_or((None, None)) | ||
389 | }, | ||
385 | ast::Stmt(_it) => (None, None), | 390 | ast::Stmt(_it) => (None, None), |
386 | _ => { | 391 | _ => { |
387 | match node.parent() { | 392 | match node.parent() { |
@@ -785,6 +790,19 @@ fn foo() { | |||
785 | } | 790 | } |
786 | 791 | ||
787 | #[test] | 792 | #[test] |
793 | fn expected_type_generic_struct_field() { | ||
794 | check_expected_type_and_name( | ||
795 | r#" | ||
796 | struct Foo<T> { a: T } | ||
797 | fn foo() -> Foo<u32> { | ||
798 | Foo { a: $0 } | ||
799 | } | ||
800 | "#, | ||
801 | expect![[r#"ty: u32, name: a"#]], | ||
802 | ) | ||
803 | } | ||
804 | |||
805 | #[test] | ||
788 | fn expected_type_struct_field_with_leading_char() { | 806 | fn expected_type_struct_field_with_leading_char() { |
789 | cov_mark::check!(expected_type_struct_field_with_leading_char); | 807 | cov_mark::check!(expected_type_struct_field_with_leading_char); |
790 | check_expected_type_and_name( | 808 | check_expected_type_and_name( |
@@ -895,4 +913,52 @@ fn foo() -> u32 { | |||
895 | expect![[r#"ty: u32, name: ?"#]], | 913 | expect![[r#"ty: u32, name: ?"#]], |
896 | ) | 914 | ) |
897 | } | 915 | } |
916 | |||
917 | #[test] | ||
918 | fn expected_type_closure_param_return() { | ||
919 | // FIXME: make this work with `|| $0` | ||
920 | check_expected_type_and_name( | ||
921 | r#" | ||
922 | fn foo() { | ||
923 | bar(|| a$0); | ||
924 | } | ||
925 | |||
926 | fn bar(f: impl FnOnce() -> u32) {} | ||
927 | #[lang = "fn_once"] | ||
928 | trait FnOnce { type Output; } | ||
929 | "#, | ||
930 | expect![[r#"ty: u32, name: ?"#]], | ||
931 | ); | ||
932 | } | ||
933 | |||
934 | #[test] | ||
935 | fn expected_type_generic_function() { | ||
936 | check_expected_type_and_name( | ||
937 | r#" | ||
938 | fn foo() { | ||
939 | bar::<u32>($0); | ||
940 | } | ||
941 | |||
942 | fn bar<T>(t: T) {} | ||
943 | "#, | ||
944 | expect![[r#"ty: u32, name: t"#]], | ||
945 | ); | ||
946 | } | ||
947 | |||
948 | #[test] | ||
949 | fn expected_type_generic_method() { | ||
950 | check_expected_type_and_name( | ||
951 | r#" | ||
952 | fn foo() { | ||
953 | S(1u32).bar($0); | ||
954 | } | ||
955 | |||
956 | struct S<T>(T); | ||
957 | impl<T> S<T> { | ||
958 | fn bar(self, t: T) {} | ||
959 | } | ||
960 | "#, | ||
961 | expect![[r#"ty: u32, name: t"#]], | ||
962 | ); | ||
963 | } | ||
898 | } | 964 | } |
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index 645349215..1152a9850 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs | |||
@@ -107,7 +107,7 @@ pub use crate::{ | |||
107 | /// identifier prefix/fuzzy match should be done higher in the stack, together | 107 | /// identifier prefix/fuzzy match should be done higher in the stack, together |
108 | /// with ordering of completions (currently this is done by the client). | 108 | /// with ordering of completions (currently this is done by the client). |
109 | /// | 109 | /// |
110 | /// # Hypothetical Completion Problem | 110 | /// # Speculative Completion Problem |
111 | /// | 111 | /// |
112 | /// There's a curious unsolved problem in the current implementation. Often, you | 112 | /// There's a curious unsolved problem in the current implementation. Often, you |
113 | /// want to compute completions on a *slightly different* text document. | 113 | /// want to compute completions on a *slightly different* text document. |
@@ -121,7 +121,7 @@ pub use crate::{ | |||
121 | /// doesn't allow such "phantom" inputs. | 121 | /// doesn't allow such "phantom" inputs. |
122 | /// | 122 | /// |
123 | /// Another case where this would be instrumental is macro expansion. We want to | 123 | /// Another case where this would be instrumental is macro expansion. We want to |
124 | /// insert a fake ident and re-expand code. There's `expand_hypothetical` as a | 124 | /// insert a fake ident and re-expand code. There's `expand_speculative` as a |
125 | /// work-around for this. | 125 | /// work-around for this. |
126 | /// | 126 | /// |
127 | /// A different use-case is completion of injection (examples and links in doc | 127 | /// A different use-case is completion of injection (examples and links in doc |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 6b04ee164..d7f96b864 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -667,6 +667,13 @@ fn foo() { A { the$0 } } | |||
667 | ), | 667 | ), |
668 | detail: "u32", | 668 | detail: "u32", |
669 | deprecated: true, | 669 | deprecated: true, |
670 | relevance: CompletionRelevance { | ||
671 | exact_name_match: false, | ||
672 | type_match: Some( | ||
673 | CouldUnify, | ||
674 | ), | ||
675 | is_local: false, | ||
676 | }, | ||
670 | }, | 677 | }, |
671 | ] | 678 | ] |
672 | "#]], | 679 | "#]], |