diff options
author | Florian Diebold <[email protected]> | 2021-05-23 17:10:40 +0100 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2021-05-23 17:45:44 +0100 |
commit | 7a0c93c58ac17b089edd8c9763fef303b7a81414 (patch) | |
tree | 49cb56219cd7aea41376c49252601264ebabcf1a | |
parent | 4a6cdd776d403bacce0a5471d77e8c76695c5bc5 (diff) |
Infer correct expected type for generic struct fields
-rw-r--r-- | crates/hir/src/lib.rs | 18 | ||||
-rw-r--r-- | crates/hir_def/src/lib.rs | 8 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 32 | ||||
-rw-r--r-- | crates/ide_completion/src/render.rs | 7 |
4 files changed, 46 insertions, 19 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index a7c42ca1e..edee99356 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -513,9 +513,9 @@ impl Field { | |||
513 | } | 513 | } |
514 | 514 | ||
515 | /// Returns the type as in the signature of the struct (i.e., with | 515 | /// Returns the type as in the signature of the struct (i.e., with |
516 | /// placeholder types for type parameters). This is good for showing | 516 | /// placeholder types for type parameters). Only use this in the context of |
517 | /// signature help, but not so good to actually get the type of the field | 517 | /// the field *definition*; if you've already got a variable of the struct |
518 | /// when you actually have a variable of the struct. | 518 | /// type, use `Type::field_type` to get to the field type. |
519 | pub fn ty(&self, db: &dyn HirDatabase) -> Type { | 519 | pub fn ty(&self, db: &dyn HirDatabase) -> Type { |
520 | let var_id = self.parent.into(); | 520 | let var_id = self.parent.into(); |
521 | let generic_def_id: GenericDefId = match self.parent { | 521 | let generic_def_id: GenericDefId = match self.parent { |
@@ -1944,6 +1944,18 @@ impl Type { | |||
1944 | } | 1944 | } |
1945 | } | 1945 | } |
1946 | 1946 | ||
1947 | pub fn field_type(&self, db: &dyn HirDatabase, field: Field) -> Option<Type> { | ||
1948 | let (adt_id, substs) = self.ty.as_adt()?; | ||
1949 | let variant_id: hir_def::VariantId = field.parent.into(); | ||
1950 | if variant_id.adt_id() != adt_id { | ||
1951 | return None; | ||
1952 | } | ||
1953 | |||
1954 | let ty = db.field_types(variant_id).get(field.id)?.clone(); | ||
1955 | let ty = ty.substitute(&Interner, substs); | ||
1956 | Some(self.derived(ty)) | ||
1957 | } | ||
1958 | |||
1947 | pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> { | 1959 | pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> { |
1948 | let (variant_id, substs) = match self.ty.kind(&Interner) { | 1960 | let (variant_id, substs) = match self.ty.kind(&Interner) { |
1949 | &TyKind::Adt(hir_ty::AdtId(AdtId::StructId(s)), ref substs) => (s.into(), substs), | 1961 | &TyKind::Adt(hir_ty::AdtId(AdtId::StructId(s)), ref substs) => (s.into(), substs), |
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index a82ea5957..70001cac8 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -485,6 +485,14 @@ impl VariantId { | |||
485 | VariantId::UnionId(it) => it.lookup(db).id.file_id(), | 485 | VariantId::UnionId(it) => it.lookup(db).id.file_id(), |
486 | } | 486 | } |
487 | } | 487 | } |
488 | |||
489 | pub fn adt_id(self) -> AdtId { | ||
490 | match self { | ||
491 | VariantId::EnumVariantId(it) => it.parent.into(), | ||
492 | VariantId::StructId(it) => it.into(), | ||
493 | VariantId::UnionId(it) => it.into(), | ||
494 | } | ||
495 | } | ||
488 | } | 496 | } |
489 | 497 | ||
490 | trait Intern { | 498 | trait Intern { |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index c929d7394..4a88a6e88 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -337,25 +337,25 @@ 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 record_ty = self.sema.type_of_expr(&ast::Expr::cast(node.parent()?)?)?; |
343 | .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf))) | 343 | let expr_field = self.token.prev_sibling_or_token()? |
344 | .map(|(f, rf)|( | 344 | .into_node() |
345 | Some(f.0.ty(self.db)), | 345 | .and_then(|node| ast::RecordExprField::cast(node))?; |
346 | rf.field_name().map(NameOrNameRef::NameRef), | 346 | let field = self.sema.resolve_record_field(&expr_field)?.0; |
347 | Some(( | ||
348 | record_ty.field_type(self.db, field), | ||
349 | expr_field.field_name().map(NameOrNameRef::NameRef), | ||
347 | )) | 350 | )) |
348 | .unwrap_or((None, None)) | 351 | })().unwrap_or((None, None)) |
349 | }, | 352 | }, |
350 | ast::RecordExprField(it) => { | 353 | ast::RecordExprField(it) => { |
351 | cov_mark::hit!(expected_type_struct_field_with_leading_char); | 354 | cov_mark::hit!(expected_type_struct_field_with_leading_char); |
352 | self.sema | 355 | ( |
353 | .resolve_record_field(&it) | 356 | it.expr().as_ref().and_then(|e| self.sema.type_of_expr(e)), |
354 | .map(|f|( | 357 | it.field_name().map(NameOrNameRef::NameRef), |
355 | Some(f.0.ty(self.db)), | 358 | ) |
356 | it.field_name().map(NameOrNameRef::NameRef), | ||
357 | )) | ||
358 | .unwrap_or((None, None)) | ||
359 | }, | 359 | }, |
360 | ast::MatchExpr(it) => { | 360 | ast::MatchExpr(it) => { |
361 | cov_mark::hit!(expected_type_match_arm_without_leading_char); | 361 | cov_mark::hit!(expected_type_match_arm_without_leading_char); |
@@ -910,7 +910,7 @@ fn foo() -> u32 { | |||
910 | } | 910 | } |
911 | 911 | ||
912 | #[test] | 912 | #[test] |
913 | fn expected_type_closure_param() { | 913 | fn expected_type_closure_param_return() { |
914 | check_expected_type_and_name( | 914 | check_expected_type_and_name( |
915 | r#" | 915 | r#" |
916 | fn foo() { | 916 | fn foo() { |
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 | "#]], |