aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Tobias Wirth <[email protected]>2021-05-03 20:34:34 +0100
committerLukas Tobias Wirth <[email protected]>2021-05-03 20:34:34 +0100
commit121bd5c533de2ae8b74de72f1b1dbbc7327d358b (patch)
tree431f6699b92b14c5363543fac3da15a24ea20ac2
parent544a93ee0815697ff42b79e54d1a7a5a743de1f9 (diff)
Make CompletionContext expected_type smarter
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs22
-rw-r--r--crates/ide_completion/src/context.rs208
2 files changed, 113 insertions, 117 deletions
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 969249df6..d8f23d1eb 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -737,28 +737,6 @@ fn f() {}
737 } 737 }
738 738
739 #[test] 739 #[test]
740 fn completes_function() {
741 check(
742 r#"
743fn foo(
744 a: i32,
745 b: i32
746) {
747
748}
749
750fn main() {
751 fo$0
752}
753"#,
754 expect![[r#"
755 fn main() fn()
756 fn foo(…) fn(i32, i32)
757 "#]],
758 );
759 }
760
761 #[test]
762 fn completes_self_enum() { 740 fn completes_self_enum() {
763 check( 741 check(
764 r#" 742 r#"
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index b005bd773..f3fcb712c 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -301,103 +301,108 @@ impl<'a> CompletionContext<'a> {
301 .find_map(ast::Impl::cast); 301 .find_map(ast::Impl::cast);
302 } 302 }
303 303
304 fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
305 let mut node = match self.token.parent() {
306 Some(it) => it,
307 None => return (None, None),
308 };
309 loop {
310 break match_ast! {
311 match node {
312 ast::LetStmt(it) => {
313 cov_mark::hit!(expected_type_let_with_leading_char);
314 cov_mark::hit!(expected_type_let_without_leading_char);
315 let ty = it.pat()
316 .and_then(|pat| self.sema.type_of_pat(&pat));
317 let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
318 ident.name().map(NameOrNameRef::Name)
319 } else {
320 None
321 };
322
323 (ty, name)
324 },
325 ast::ArgList(_it) => {
326 cov_mark::hit!(expected_type_fn_param_with_leading_char);
327 cov_mark::hit!(expected_type_fn_param_without_leading_char);
328 ActiveParameter::at_token(
329 &self.sema,
330 self.token.clone(),
331 ).map(|ap| {
332 let name = ap.ident().map(NameOrNameRef::Name);
333 (Some(ap.ty), name)
334 })
335 .unwrap_or((None, None))
336 },
337 ast::RecordExprFieldList(_it) => {
338 cov_mark::hit!(expected_type_struct_field_without_leading_char);
339 self.token.prev_sibling_or_token()
340 .and_then(|se| se.into_node())
341 .and_then(|node| ast::RecordExprField::cast(node))
342 .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
343 .map(|(f, rf)|(
344 Some(f.0.ty(self.db)),
345 rf.field_name().map(NameOrNameRef::NameRef),
346 ))
347 .unwrap_or((None, None))
348 },
349 ast::RecordExprField(it) => {
350 cov_mark::hit!(expected_type_struct_field_with_leading_char);
351 self.sema
352 .resolve_record_field(&it)
353 .map(|f|(
354 Some(f.0.ty(self.db)),
355 it.field_name().map(NameOrNameRef::NameRef),
356 ))
357 .unwrap_or((None, None))
358 },
359 ast::MatchExpr(it) => {
360 cov_mark::hit!(expected_type_match_arm_without_leading_char);
361 let ty = it.expr()
362 .and_then(|e| self.sema.type_of_expr(&e));
363 (ty, None)
364 },
365 ast::IfExpr(it) => {
366 cov_mark::hit!(expected_type_if_let_without_leading_char);
367 let ty = it.condition()
368 .and_then(|cond| cond.expr())
369 .and_then(|e| self.sema.type_of_expr(&e));
370 (ty, None)
371 },
372 ast::IdentPat(it) => {
373 cov_mark::hit!(expected_type_if_let_with_leading_char);
374 cov_mark::hit!(expected_type_match_arm_with_leading_char);
375 let ty = self.sema.type_of_pat(&ast::Pat::from(it));
376 (ty, None)
377 },
378 ast::Fn(it) => {
379 cov_mark::hit!(expected_type_fn_ret_with_leading_char);
380 cov_mark::hit!(expected_type_fn_ret_without_leading_char);
381 let def = self.sema.to_def(&it);
382 (def.map(|def| def.ret_type(self.db)), None)
383 },
384 ast::Stmt(it) => (None, None),
385 _ => {
386 match node.parent() {
387 Some(n) => {
388 node = n;
389 continue;
390 },
391 None => (None, None),
392 }
393 },
394 }
395 };
396 }
397 }
398
304 fn fill( 399 fn fill(
305 &mut self, 400 &mut self,
306 original_file: &SyntaxNode, 401 original_file: &SyntaxNode,
307 file_with_fake_ident: SyntaxNode, 402 file_with_fake_ident: SyntaxNode,
308 offset: TextSize, 403 offset: TextSize,
309 ) { 404 ) {
310 let (expected_type, expected_name) = { 405 let (expected_type, expected_name) = self.expected_type_and_name();
311 let mut node = match self.token.parent() {
312 Some(it) => it,
313 None => return,
314 };
315 loop {
316 break match_ast! {
317 match node {
318 ast::LetStmt(it) => {
319 cov_mark::hit!(expected_type_let_with_leading_char);
320 cov_mark::hit!(expected_type_let_without_leading_char);
321 let ty = it.pat()
322 .and_then(|pat| self.sema.type_of_pat(&pat));
323 let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
324 ident.name().map(NameOrNameRef::Name)
325 } else {
326 None
327 };
328
329 (ty, name)
330 },
331 ast::ArgList(_it) => {
332 cov_mark::hit!(expected_type_fn_param_with_leading_char);
333 cov_mark::hit!(expected_type_fn_param_without_leading_char);
334 ActiveParameter::at_token(
335 &self.sema,
336 self.token.clone(),
337 ).map(|ap| {
338 let name = ap.ident().map(NameOrNameRef::Name);
339 (Some(ap.ty), name)
340 })
341 .unwrap_or((None, None))
342 },
343 ast::RecordExprFieldList(_it) => {
344 cov_mark::hit!(expected_type_struct_field_without_leading_char);
345 self.token.prev_sibling_or_token()
346 .and_then(|se| se.into_node())
347 .and_then(|node| ast::RecordExprField::cast(node))
348 .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
349 .map(|(f, rf)|(
350 Some(f.0.ty(self.db)),
351 rf.field_name().map(NameOrNameRef::NameRef),
352 ))
353 .unwrap_or((None, None))
354 },
355 ast::RecordExprField(it) => {
356 cov_mark::hit!(expected_type_struct_field_with_leading_char);
357 self.sema
358 .resolve_record_field(&it)
359 .map(|f|(
360 Some(f.0.ty(self.db)),
361 it.field_name().map(NameOrNameRef::NameRef),
362 ))
363 .unwrap_or((None, None))
364 },
365 ast::MatchExpr(it) => {
366 cov_mark::hit!(expected_type_match_arm_without_leading_char);
367 let ty = it.expr()
368 .and_then(|e| self.sema.type_of_expr(&e));
369
370 (ty, None)
371 },
372 ast::IdentPat(it) => {
373 cov_mark::hit!(expected_type_if_let_with_leading_char);
374 cov_mark::hit!(expected_type_match_arm_with_leading_char);
375 let ty = self.sema.type_of_pat(&ast::Pat::from(it));
376
377 (ty, None)
378 },
379 ast::Fn(_it) => {
380 cov_mark::hit!(expected_type_fn_ret_with_leading_char);
381 cov_mark::hit!(expected_type_fn_ret_without_leading_char);
382 let ty = self.token.ancestors()
383 .find_map(|ancestor| ast::Expr::cast(ancestor))
384 .and_then(|expr| self.sema.type_of_expr(&expr));
385
386 (ty, None)
387 },
388 _ => {
389 match node.parent() {
390 Some(n) => {
391 node = n;
392 continue;
393 },
394 None => (None, None),
395 }
396 },
397 }
398 };
399 }
400 };
401 self.expected_type = expected_type; 406 self.expected_type = expected_type;
402 self.expected_name = expected_name; 407 self.expected_name = expected_name;
403 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); 408 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
@@ -802,6 +807,7 @@ fn foo() {
802 807
803 #[test] 808 #[test]
804 fn expected_type_if_let_without_leading_char() { 809 fn expected_type_if_let_without_leading_char() {
810 cov_mark::check!(expected_type_if_let_without_leading_char);
805 check_expected_type_and_name( 811 check_expected_type_and_name(
806 r#" 812 r#"
807enum Foo { Bar, Baz, Quux } 813enum Foo { Bar, Baz, Quux }
@@ -811,8 +817,8 @@ fn foo() {
811 if let $0 = f { } 817 if let $0 = f { }
812} 818}
813"#, 819"#,
814 expect![[r#"ty: (), name: ?"#]], 820 expect![[r#"ty: Foo, name: ?"#]],
815 ) // FIXME should be `ty: u32, name: ?` 821 )
816 } 822 }
817 823
818 #[test] 824 #[test]
@@ -840,8 +846,8 @@ fn foo() -> u32 {
840 $0 846 $0
841} 847}
842"#, 848"#,
843 expect![[r#"ty: (), name: ?"#]], 849 expect![[r#"ty: u32, name: ?"#]],
844 ) // FIXME this should be `ty: u32, name: ?` 850 )
845 } 851 }
846 852
847 #[test] 853 #[test]
@@ -856,4 +862,16 @@ fn foo() -> u32 {
856 expect![[r#"ty: u32, name: ?"#]], 862 expect![[r#"ty: u32, name: ?"#]],
857 ) 863 )
858 } 864 }
865
866 #[test]
867 fn expected_type_fn_ret_fn_ref_fully_typed() {
868 check_expected_type_and_name(
869 r#"
870fn foo() -> u32 {
871 foo$0
872}
873"#,
874 expect![[r#"ty: u32, name: ?"#]],
875 )
876 }
859} 877}