mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 06:41:48 +00:00
Diagnose incorrect and private fields in record structs
This commit is contained in:
parent
55c75450fb
commit
8f5fee4a5a
10 changed files with 192 additions and 81 deletions
|
@ -194,7 +194,7 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
|
|||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum InferenceDiagnostic {
|
||||
NoSuchField {
|
||||
expr: ExprId,
|
||||
field: ExprOrPatId,
|
||||
private: bool,
|
||||
},
|
||||
PrivateField {
|
||||
|
|
|
@ -527,7 +527,7 @@ impl InferenceContext<'_> {
|
|||
self.write_variant_resolution(tgt_expr.into(), variant);
|
||||
}
|
||||
match def_id {
|
||||
Some(_) if fields.is_empty() => {}
|
||||
_ if fields.is_empty() => {}
|
||||
Some(def) => {
|
||||
let field_types = self.db.field_types(def);
|
||||
let variant_data = def.variant_data(self.db.upcast());
|
||||
|
@ -542,16 +542,16 @@ impl InferenceContext<'_> {
|
|||
) {
|
||||
self.push_diagnostic(
|
||||
InferenceDiagnostic::NoSuchField {
|
||||
expr: field.expr,
|
||||
field: field.expr.into(),
|
||||
private: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
Some(FieldId { parent: def, local_id })
|
||||
Some(local_id)
|
||||
}
|
||||
None => {
|
||||
self.push_diagnostic(InferenceDiagnostic::NoSuchField {
|
||||
expr: field.expr,
|
||||
field: field.expr.into(),
|
||||
private: false,
|
||||
});
|
||||
None
|
||||
|
@ -559,7 +559,7 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
};
|
||||
let field_ty = field_def.map_or(self.err_ty(), |it| {
|
||||
field_types[it.local_id].clone().substitute(Interner, &substs)
|
||||
field_types[it].clone().substitute(Interner, &substs)
|
||||
});
|
||||
|
||||
// Field type might have some unknown types
|
||||
|
@ -1154,7 +1154,7 @@ impl InferenceContext<'_> {
|
|||
Expr::Underscore => rhs_ty.clone(),
|
||||
_ => {
|
||||
// `lhs` is a place expression, a unit struct, or an enum variant.
|
||||
let lhs_ty = self.infer_expr(lhs, &Expectation::none());
|
||||
let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none());
|
||||
|
||||
// This is the only branch where this function may coerce any type.
|
||||
// We are returning early to avoid the unifiability check below.
|
||||
|
|
|
@ -92,24 +92,51 @@ impl InferenceContext<'_> {
|
|||
let substs =
|
||||
ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner));
|
||||
|
||||
let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
|
||||
let (pre, post) = match ellipsis {
|
||||
Some(idx) => subs.split_at(idx),
|
||||
None => (subs, &[][..]),
|
||||
};
|
||||
let post_idx_offset = field_tys.iter().count().saturating_sub(post.len());
|
||||
match def {
|
||||
_ if subs.len() == 0 => {}
|
||||
Some(def) => {
|
||||
let field_types = self.db.field_types(def);
|
||||
let variant_data = def.variant_data(self.db.upcast());
|
||||
let visibilities = self.db.field_visibilities(def);
|
||||
|
||||
let pre_iter = pre.iter().enumerate();
|
||||
let post_iter = (post_idx_offset..).zip(post.iter());
|
||||
for (i, &subpat) in pre_iter.chain(post_iter) {
|
||||
let expected_ty = var_data
|
||||
.as_ref()
|
||||
.and_then(|d| d.field(&Name::new_tuple_field(i)))
|
||||
.map_or(self.err_ty(), |field| {
|
||||
field_tys[field].clone().substitute(Interner, &substs)
|
||||
});
|
||||
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
||||
T::infer(self, subpat, &expected_ty, default_bm);
|
||||
let (pre, post) = match ellipsis {
|
||||
Some(idx) => subs.split_at(idx),
|
||||
None => (subs, &[][..]),
|
||||
};
|
||||
let post_idx_offset = field_types.iter().count().saturating_sub(post.len());
|
||||
|
||||
let pre_iter = pre.iter().enumerate();
|
||||
let post_iter = (post_idx_offset..).zip(post.iter());
|
||||
|
||||
for (i, &subpat) in pre_iter.chain(post_iter) {
|
||||
let field_def = {
|
||||
match variant_data.field(&Name::new_tuple_field(i)) {
|
||||
Some(local_id) => {
|
||||
if !visibilities[local_id]
|
||||
.is_visible_from(self.db.upcast(), self.resolver.module())
|
||||
{
|
||||
// FIXME(DIAGNOSE): private tuple field
|
||||
}
|
||||
Some(local_id)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
};
|
||||
|
||||
let expected_ty = field_def.map_or(self.err_ty(), |f| {
|
||||
field_types[f].clone().substitute(Interner, &substs)
|
||||
});
|
||||
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
||||
|
||||
T::infer(self, subpat, &expected_ty, default_bm);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let err_ty = self.err_ty();
|
||||
for &inner in subs {
|
||||
T::infer(self, inner, &err_ty, default_bm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty
|
||||
|
@ -122,7 +149,7 @@ impl InferenceContext<'_> {
|
|||
expected: &Ty,
|
||||
default_bm: T::BindingMode,
|
||||
id: T,
|
||||
subs: impl Iterator<Item = (Name, T)>,
|
||||
subs: impl Iterator<Item = (Name, T)> + ExactSizeIterator,
|
||||
) -> Ty {
|
||||
let (ty, def) = self.resolve_variant(path, false);
|
||||
if let Some(variant) = def {
|
||||
|
@ -134,17 +161,51 @@ impl InferenceContext<'_> {
|
|||
let substs =
|
||||
ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner));
|
||||
|
||||
let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
|
||||
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
|
||||
match def {
|
||||
_ if subs.len() == 0 => {}
|
||||
Some(def) => {
|
||||
let field_types = self.db.field_types(def);
|
||||
let variant_data = def.variant_data(self.db.upcast());
|
||||
let visibilities = self.db.field_visibilities(def);
|
||||
|
||||
for (name, inner) in subs {
|
||||
let expected_ty = var_data
|
||||
.as_ref()
|
||||
.and_then(|it| it.field(&name))
|
||||
.map_or(self.err_ty(), |f| field_tys[f].clone().substitute(Interner, &substs));
|
||||
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
||||
for (name, inner) in subs {
|
||||
let field_def = {
|
||||
match variant_data.field(&name) {
|
||||
Some(local_id) => {
|
||||
if !visibilities[local_id]
|
||||
.is_visible_from(self.db.upcast(), self.resolver.module())
|
||||
{
|
||||
self.push_diagnostic(InferenceDiagnostic::NoSuchField {
|
||||
field: inner.into(),
|
||||
private: true,
|
||||
});
|
||||
}
|
||||
Some(local_id)
|
||||
}
|
||||
None => {
|
||||
self.push_diagnostic(InferenceDiagnostic::NoSuchField {
|
||||
field: inner.into(),
|
||||
private: false,
|
||||
});
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
T::infer(self, inner, &expected_ty, default_bm);
|
||||
let expected_ty = field_def.map_or(self.err_ty(), |f| {
|
||||
field_types[f].clone().substitute(Interner, &substs)
|
||||
});
|
||||
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
||||
|
||||
T::infer(self, inner, &expected_ty, default_bm);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let err_ty = self.err_ty();
|
||||
for (_, inner) in subs {
|
||||
T::infer(self, inner, &err_ty, default_bm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty
|
||||
|
|
|
@ -166,8 +166,8 @@ impl<V, T> ProjectionElem<V, T> {
|
|||
TyKind::Adt(_, subst) => {
|
||||
db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
|
||||
}
|
||||
_ => {
|
||||
never!("Only adt has field");
|
||||
ty => {
|
||||
never!("Only adt has field, found {:?}", ty);
|
||||
return TyKind::Error.intern(Interner);
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue