aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r--crates/ide_completion/src/context.rs116
-rw-r--r--crates/ide_completion/src/lib.rs4
-rw-r--r--crates/ide_completion/src/render.rs7
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#"
796struct Foo<T> { a: T }
797fn 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#"
922fn foo() {
923 bar(|| a$0);
924}
925
926fn bar(f: impl FnOnce() -> u32) {}
927#[lang = "fn_once"]
928trait 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#"
938fn foo() {
939 bar::<u32>($0);
940}
941
942fn 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#"
952fn foo() {
953 S(1u32).bar($0);
954}
955
956struct S<T>(T);
957impl<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 "#]],