add record pat missing field diagnostic

This commit is contained in:
Josh Mcguigan 2020-04-08 20:23:51 -07:00
parent 176f7f6117
commit e63315b8f1
7 changed files with 159 additions and 57 deletions

View file

@ -9,7 +9,7 @@ use rustc_hash::FxHashSet;
use crate::{
db::HirDatabase,
diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr},
diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields},
utils::variant_data,
ApplicationTy, InferenceResult, Ty, TypeCtor,
_match::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
@ -49,39 +49,97 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
if let Some((variant_def, missed_fields, true)) =
record_literal_missing_fields(db, &self.infer, id, expr)
{
// XXX: only look at source_map if we do have missing fields
let (_, source_map) = db.body_with_source_map(self.func.into());
if let Ok(source_ptr) = source_map.expr_syntax(id) {
if let Some(expr) = source_ptr.value.left() {
let root = source_ptr.file_syntax(db.upcast());
if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) {
if let Some(field_list) = record_lit.record_field_list() {
let variant_data = variant_data(db.upcast(), variant_def);
let missed_fields = missed_fields
.into_iter()
.map(|idx| variant_data.fields()[idx].name.clone())
.collect();
self.sink.push(MissingFields {
file: source_ptr.file_id,
field_list: AstPtr::new(&field_list),
missed_fields,
})
}
}
}
}
self.create_record_literal_missing_fields_diagnostic(
id,
db,
variant_def,
missed_fields,
);
}
if let Expr::Match { expr, arms } = expr {
self.validate_match(id, *expr, arms, db, self.infer.clone());
}
}
for (id, pat) in body.pats.iter() {
if let Some((variant_def, missed_fields, true)) =
record_pattern_missing_fields(db, &self.infer, id, pat)
{
self.create_record_pattern_missing_fields_diagnostic(
id,
db,
variant_def,
missed_fields,
);
}
}
let body_expr = &body[body.body_expr];
if let Expr::Block { tail: Some(t), .. } = body_expr {
self.validate_results_in_tail_expr(body.body_expr, *t, db);
}
}
fn create_record_literal_missing_fields_diagnostic(
&mut self,
id: ExprId,
db: &dyn HirDatabase,
variant_def: VariantId,
missed_fields: Vec<LocalStructFieldId>,
) {
// XXX: only look at source_map if we do have missing fields
let (_, source_map) = db.body_with_source_map(self.func.into());
if let Ok(source_ptr) = source_map.expr_syntax(id) {
if let Some(expr) = source_ptr.value.left() {
let root = source_ptr.file_syntax(db.upcast());
if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) {
if let Some(field_list) = record_lit.record_field_list() {
let variant_data = variant_data(db.upcast(), variant_def);
let missed_fields = missed_fields
.into_iter()
.map(|idx| variant_data.fields()[idx].name.clone())
.collect();
self.sink.push(MissingFields {
file: source_ptr.file_id,
field_list: AstPtr::new(&field_list),
missed_fields,
})
}
}
}
}
}
fn create_record_pattern_missing_fields_diagnostic(
&mut self,
id: PatId,
db: &dyn HirDatabase,
variant_def: VariantId,
missed_fields: Vec<LocalStructFieldId>,
) {
// XXX: only look at source_map if we do have missing fields
let (_, source_map) = db.body_with_source_map(self.func.into());
if let Ok(source_ptr) = source_map.pat_syntax(id) {
if let Some(expr) = source_ptr.value.left() {
let root = source_ptr.file_syntax(db.upcast());
if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
if let Some(field_list) = record_pat.record_field_pat_list() {
let variant_data = variant_data(db.upcast(), variant_def);
let missed_fields = missed_fields
.into_iter()
.map(|idx| variant_data.fields()[idx].name.clone())
.collect();
self.sink.push(MissingPatFields {
file: source_ptr.file_id,
field_list: AstPtr::new(&field_list),
missed_fields,
})
}
}
}
}
}
fn validate_match(
&mut self,
id: ExprId,
@ -232,9 +290,9 @@ pub fn record_pattern_missing_fields(
infer: &InferenceResult,
id: PatId,
pat: &Pat,
) -> Option<(VariantId, Vec<LocalStructFieldId>)> {
let fields = match pat {
Pat::Record { path: _, args } => args,
) -> Option<(VariantId, Vec<LocalStructFieldId>, /*exhaustive*/ bool)> {
let (fields, exhaustive) = match pat {
Pat::Record { path: _, args, ellipsis } => (args, !ellipsis),
_ => return None,
};
@ -254,5 +312,5 @@ pub fn record_pattern_missing_fields(
if missed_fields.is_empty() {
return None;
}
Some((variant_def, missed_fields))
Some((variant_def, missed_fields, exhaustive))
}