Skip to content

Commit 7a0c93c

Browse files
committed
Infer correct expected type for generic struct fields
1 parent 4a6cdd7 commit 7a0c93c

File tree

4 files changed

+46
-19
lines changed

4 files changed

+46
-19
lines changed

crates/hir/src/lib.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -513,9 +513,9 @@ impl Field {
513513
}
514514

515515
/// Returns the type as in the signature of the struct (i.e., with
516-
/// placeholder types for type parameters). This is good for showing
517-
/// signature help, but not so good to actually get the type of the field
518-
/// when you actually have a variable of the struct.
516+
/// placeholder types for type parameters). Only use this in the context of
517+
/// the field *definition*; if you've already got a variable of the struct
518+
/// type, use `Type::field_type` to get to the field type.
519519
pub fn ty(&self, db: &dyn HirDatabase) -> Type {
520520
let var_id = self.parent.into();
521521
let generic_def_id: GenericDefId = match self.parent {
@@ -1944,6 +1944,18 @@ impl Type {
19441944
}
19451945
}
19461946

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+
19471959
pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> {
19481960
let (variant_id, substs) = match self.ty.kind(&Interner) {
19491961
&TyKind::Adt(hir_ty::AdtId(AdtId::StructId(s)), ref substs) => (s.into(), substs),

crates/hir_def/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,14 @@ impl VariantId {
485485
VariantId::UnionId(it) => it.lookup(db).id.file_id(),
486486
}
487487
}
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+
}
488496
}
489497

490498
trait Intern {

crates/ide_completion/src/context.rs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -337,25 +337,25 @@ impl<'a> CompletionContext<'a> {
337337
},
338338
ast::RecordExprFieldList(_it) => {
339339
cov_mark::hit!(expected_type_struct_field_without_leading_char);
340-
self.token.prev_sibling_or_token()
341-
.and_then(|se| se.into_node())
342-
.and_then(|node| ast::RecordExprField::cast(node))
343-
.and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
344-
.map(|(f, rf)|(
345-
Some(f.0.ty(self.db)),
346-
rf.field_name().map(NameOrNameRef::NameRef),
340+
// wouldn't try {} be nice...
341+
(|| {
342+
let record_ty = self.sema.type_of_expr(&ast::Expr::cast(node.parent()?)?)?;
343+
let expr_field = self.token.prev_sibling_or_token()?
344+
.into_node()
345+
.and_then(|node| ast::RecordExprField::cast(node))?;
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),
347350
))
348-
.unwrap_or((None, None))
351+
})().unwrap_or((None, None))
349352
},
350353
ast::RecordExprField(it) => {
351354
cov_mark::hit!(expected_type_struct_field_with_leading_char);
352-
self.sema
353-
.resolve_record_field(&it)
354-
.map(|f|(
355-
Some(f.0.ty(self.db)),
356-
it.field_name().map(NameOrNameRef::NameRef),
357-
))
358-
.unwrap_or((None, None))
355+
(
356+
it.expr().as_ref().and_then(|e| self.sema.type_of_expr(e)),
357+
it.field_name().map(NameOrNameRef::NameRef),
358+
)
359359
},
360360
ast::MatchExpr(it) => {
361361
cov_mark::hit!(expected_type_match_arm_without_leading_char);
@@ -910,7 +910,7 @@ fn foo() -> u32 {
910910
}
911911

912912
#[test]
913-
fn expected_type_closure_param() {
913+
fn expected_type_closure_param_return() {
914914
check_expected_type_and_name(
915915
r#"
916916
fn foo() {

crates/ide_completion/src/render.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,13 @@ fn foo() { A { the$0 } }
667667
),
668668
detail: "u32",
669669
deprecated: true,
670+
relevance: CompletionRelevance {
671+
exact_name_match: false,
672+
type_match: Some(
673+
CouldUnify,
674+
),
675+
is_local: false,
676+
},
670677
},
671678
]
672679
"#]],

0 commit comments

Comments
 (0)