diff options
author | Lukas Tobias Wirth <[email protected]> | 2021-05-03 20:34:34 +0100 |
---|---|---|
committer | Lukas Tobias Wirth <[email protected]> | 2021-05-03 20:34:34 +0100 |
commit | 121bd5c533de2ae8b74de72f1b1dbbc7327d358b (patch) | |
tree | 431f6699b92b14c5363543fac3da15a24ea20ac2 /crates | |
parent | 544a93ee0815697ff42b79e54d1a7a5a743de1f9 (diff) |
Make CompletionContext expected_type smarter
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ide_completion/src/completions/qualified_path.rs | 22 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 208 |
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#" | ||
743 | fn foo( | ||
744 | a: i32, | ||
745 | b: i32 | ||
746 | ) { | ||
747 | |||
748 | } | ||
749 | |||
750 | fn 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#" |
807 | enum Foo { Bar, Baz, Quux } | 813 | enum 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#" | ||
870 | fn foo() -> u32 { | ||
871 | foo$0 | ||
872 | } | ||
873 | "#, | ||
874 | expect![[r#"ty: u32, name: ?"#]], | ||
875 | ) | ||
876 | } | ||
859 | } | 877 | } |