aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2021-05-23 17:10:40 +0100
committerFlorian Diebold <[email protected]>2021-05-23 17:45:44 +0100
commit7a0c93c58ac17b089edd8c9763fef303b7a81414 (patch)
tree49cb56219cd7aea41376c49252601264ebabcf1a /crates
parent4a6cdd776d403bacce0a5471d77e8c76695c5bc5 (diff)
Infer correct expected type for generic struct fields
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/lib.rs18
-rw-r--r--crates/hir_def/src/lib.rs8
-rw-r--r--crates/ide_completion/src/context.rs32
-rw-r--r--crates/ide_completion/src/render.rs7
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
490trait Intern { 498trait 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#"
916fn foo() { 916fn 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 "#]],