Skip to content

Commit cc6d761

Browse files
bors[bot]matklad
andauthored
Merge #9246
9246: internal: unified missing fields diagnostic r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
2 parents 3f53a5d + 6383252 commit cc6d761

File tree

8 files changed

+342
-401
lines changed

8 files changed

+342
-401
lines changed

crates/hir/src/diagnostics.rs

Lines changed: 4 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use std::any::Any;
77

88
use cfg::{CfgExpr, CfgOptions, DnfExpr};
9+
use either::Either;
910
use hir_def::path::ModPath;
1011
use hir_expand::{name::Name, HirFileId, InFile};
1112
use stdx::format_to;
@@ -16,7 +17,7 @@ pub use crate::diagnostics_sink::{
1617
};
1718

1819
macro_rules! diagnostics {
19-
($($diag:ident)*) => {
20+
($($diag:ident),*) => {
2021
pub enum AnyDiagnostic {$(
2122
$diag(Box<$diag>),
2223
)*}
@@ -31,7 +32,7 @@ macro_rules! diagnostics {
3132
};
3233
}
3334

34-
diagnostics![UnresolvedModule];
35+
diagnostics![UnresolvedModule, MissingFields];
3536

3637
#[derive(Debug)]
3738
pub struct UnresolvedModule {
@@ -321,102 +322,14 @@ impl Diagnostic for MissingUnsafe {
321322
}
322323
}
323324

324-
// Diagnostic: missing-structure-fields
325-
//
326-
// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
327-
//
328-
// Example:
329-
//
330-
// ```rust
331-
// struct A { a: u8, b: u8 }
332-
//
333-
// let a = A { a: 10 };
334-
// ```
335325
#[derive(Debug)]
336326
pub struct MissingFields {
337327
pub file: HirFileId,
338-
pub field_list_parent: AstPtr<ast::RecordExpr>,
339-
pub field_list_parent_path: Option<AstPtr<ast::Path>>,
340-
pub missed_fields: Vec<Name>,
341-
}
342-
343-
impl Diagnostic for MissingFields {
344-
fn code(&self) -> DiagnosticCode {
345-
DiagnosticCode("missing-structure-fields")
346-
}
347-
fn message(&self) -> String {
348-
let mut buf = String::from("Missing structure fields:\n");
349-
for field in &self.missed_fields {
350-
format_to!(buf, "- {}\n", field);
351-
}
352-
buf
353-
}
354-
355-
fn display_source(&self) -> InFile<SyntaxNodePtr> {
356-
InFile {
357-
file_id: self.file,
358-
value: self
359-
.field_list_parent_path
360-
.clone()
361-
.map(SyntaxNodePtr::from)
362-
.unwrap_or_else(|| self.field_list_parent.clone().into()),
363-
}
364-
}
365-
366-
fn as_any(&self) -> &(dyn Any + Send + 'static) {
367-
self
368-
}
369-
}
370-
371-
// Diagnostic: missing-pat-fields
372-
//
373-
// This diagnostic is triggered if pattern lacks some fields that exist in the corresponding structure.
374-
//
375-
// Example:
376-
//
377-
// ```rust
378-
// struct A { a: u8, b: u8 }
379-
//
380-
// let a = A { a: 10, b: 20 };
381-
//
382-
// if let A { a } = a {
383-
// // ...
384-
// }
385-
// ```
386-
#[derive(Debug)]
387-
pub struct MissingPatFields {
388-
pub file: HirFileId,
389-
pub field_list_parent: AstPtr<ast::RecordPat>,
328+
pub field_list_parent: Either<AstPtr<ast::RecordExpr>, AstPtr<ast::RecordPat>>,
390329
pub field_list_parent_path: Option<AstPtr<ast::Path>>,
391330
pub missed_fields: Vec<Name>,
392331
}
393332

394-
impl Diagnostic for MissingPatFields {
395-
fn code(&self) -> DiagnosticCode {
396-
DiagnosticCode("missing-pat-fields")
397-
}
398-
fn message(&self) -> String {
399-
let mut buf = String::from("Missing structure fields:\n");
400-
for field in &self.missed_fields {
401-
format_to!(buf, "- {}\n", field);
402-
}
403-
buf
404-
}
405-
fn display_source(&self) -> InFile<SyntaxNodePtr> {
406-
InFile {
407-
file_id: self.file,
408-
value: self
409-
.field_list_parent_path
410-
.clone()
411-
.map(SyntaxNodePtr::from)
412-
.unwrap_or_else(|| self.field_list_parent.clone().into()),
413-
}
414-
}
415-
fn as_any(&self) -> &(dyn Any + Send + 'static) {
416-
self
417-
}
418-
}
419-
420333
// Diagnostic: replace-filter-map-next-with-find-map
421334
//
422335
// This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`.

crates/hir/src/lib.rs

Lines changed: 68 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ pub use crate::{
8888
diagnostics::{
8989
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, InternalBailedOut, MacroError,
9090
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
91-
MissingPatFields, MissingUnsafe, NoSuchField, RemoveThisSemicolon,
92-
ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro, UnresolvedExternCrate,
93-
UnresolvedImport, UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
91+
MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
92+
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
93+
UnresolvedModule, UnresolvedProcMacro,
9494
},
9595
has_source::HasSource,
9696
semantics::{PathResolution, Semantics, SemanticsScope},
@@ -609,23 +609,21 @@ impl Module {
609609
}
610610
for decl in self.declarations(db) {
611611
match decl {
612-
crate::ModuleDef::Function(f) => f.diagnostics(db, sink, internal_diagnostics),
613-
crate::ModuleDef::Module(m) => {
612+
ModuleDef::Function(f) => acc.extend(f.diagnostics(db, sink, internal_diagnostics)),
613+
ModuleDef::Module(m) => {
614614
// Only add diagnostics from inline modules
615615
if def_map[m.id.local_id].origin.is_inline() {
616616
acc.extend(m.diagnostics(db, sink, internal_diagnostics))
617617
}
618618
}
619-
_ => {
620-
decl.diagnostics(db, sink);
621-
}
619+
_ => decl.diagnostics(db, sink),
622620
}
623621
}
624622

625623
for impl_def in self.impl_defs(db) {
626624
for item in impl_def.items(db) {
627625
if let AssocItem::Function(f) = item {
628-
f.diagnostics(db, sink, internal_diagnostics);
626+
acc.extend(f.diagnostics(db, sink, internal_diagnostics));
629627
}
630628
}
631629
}
@@ -1033,7 +1031,8 @@ impl Function {
10331031
db: &dyn HirDatabase,
10341032
sink: &mut DiagnosticSink,
10351033
internal_diagnostics: bool,
1036-
) {
1034+
) -> Vec<AnyDiagnostic> {
1035+
let mut acc: Vec<AnyDiagnostic> = Vec::new();
10371036
let krate = self.module(db).id.krate();
10381037

10391038
let source_map = db.body_with_source_map(self.id.into()).1;
@@ -1099,64 +1098,70 @@ impl Function {
10991098
BodyValidationDiagnostic::collect(db, self.id.into(), internal_diagnostics)
11001099
{
11011100
match diagnostic {
1102-
BodyValidationDiagnostic::RecordLiteralMissingFields {
1103-
record_expr,
1101+
BodyValidationDiagnostic::RecordMissingFields {
1102+
record,
11041103
variant,
11051104
missed_fields,
1106-
} => match source_map.expr_syntax(record_expr) {
1107-
Ok(source_ptr) => {
1108-
let root = source_ptr.file_syntax(db.upcast());
1109-
if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root)
1110-
{
1111-
if let Some(_) = record_expr.record_expr_field_list() {
1112-
let variant_data = variant.variant_data(db.upcast());
1113-
let missed_fields = missed_fields
1114-
.into_iter()
1115-
.map(|idx| variant_data.fields()[idx].name.clone())
1116-
.collect();
1117-
sink.push(MissingFields {
1118-
file: source_ptr.file_id,
1119-
field_list_parent: AstPtr::new(record_expr),
1120-
field_list_parent_path: record_expr
1121-
.path()
1122-
.map(|path| AstPtr::new(&path)),
1123-
missed_fields,
1124-
})
1105+
} => {
1106+
let variant_data = variant.variant_data(db.upcast());
1107+
let missed_fields = missed_fields
1108+
.into_iter()
1109+
.map(|idx| variant_data.fields()[idx].name.clone())
1110+
.collect();
1111+
1112+
match record {
1113+
Either::Left(record_expr) => match source_map.expr_syntax(record_expr) {
1114+
Ok(source_ptr) => {
1115+
let root = source_ptr.file_syntax(db.upcast());
1116+
if let ast::Expr::RecordExpr(record_expr) =
1117+
&source_ptr.value.to_node(&root)
1118+
{
1119+
if let Some(_) = record_expr.record_expr_field_list() {
1120+
acc.push(
1121+
MissingFields {
1122+
file: source_ptr.file_id,
1123+
field_list_parent: Either::Left(AstPtr::new(
1124+
record_expr,
1125+
)),
1126+
field_list_parent_path: record_expr
1127+
.path()
1128+
.map(|path| AstPtr::new(&path)),
1129+
missed_fields,
1130+
}
1131+
.into(),
1132+
)
1133+
}
1134+
}
11251135
}
1126-
}
1127-
}
1128-
Err(SyntheticSyntax) => (),
1129-
},
1130-
BodyValidationDiagnostic::RecordPatMissingFields {
1131-
record_pat,
1132-
variant,
1133-
missed_fields,
1134-
} => match source_map.pat_syntax(record_pat) {
1135-
Ok(source_ptr) => {
1136-
if let Some(expr) = source_ptr.value.as_ref().left() {
1137-
let root = source_ptr.file_syntax(db.upcast());
1138-
if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
1139-
if let Some(_) = record_pat.record_pat_field_list() {
1140-
let variant_data = variant.variant_data(db.upcast());
1141-
let missed_fields = missed_fields
1142-
.into_iter()
1143-
.map(|idx| variant_data.fields()[idx].name.clone())
1144-
.collect();
1145-
sink.push(MissingPatFields {
1146-
file: source_ptr.file_id,
1147-
field_list_parent: AstPtr::new(&record_pat),
1148-
field_list_parent_path: record_pat
1149-
.path()
1150-
.map(|path| AstPtr::new(&path)),
1151-
missed_fields,
1152-
})
1136+
Err(SyntheticSyntax) => (),
1137+
},
1138+
Either::Right(record_pat) => match source_map.pat_syntax(record_pat) {
1139+
Ok(source_ptr) => {
1140+
if let Some(expr) = source_ptr.value.as_ref().left() {
1141+
let root = source_ptr.file_syntax(db.upcast());
1142+
if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
1143+
if let Some(_) = record_pat.record_pat_field_list() {
1144+
acc.push(
1145+
MissingFields {
1146+
file: source_ptr.file_id,
1147+
field_list_parent: Either::Right(AstPtr::new(
1148+
&record_pat,
1149+
)),
1150+
field_list_parent_path: record_pat
1151+
.path()
1152+
.map(|path| AstPtr::new(&path)),
1153+
missed_fields,
1154+
}
1155+
.into(),
1156+
)
1157+
}
1158+
}
11531159
}
11541160
}
1155-
}
1161+
Err(SyntheticSyntax) => (),
1162+
},
11561163
}
1157-
Err(SyntheticSyntax) => (),
1158-
},
1159-
1164+
}
11601165
BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { method_call_expr } => {
11611166
if let Ok(next_source_ptr) = source_map.expr_syntax(method_call_expr) {
11621167
sink.push(ReplaceFilterMapNextWithFindMap {
@@ -1234,6 +1239,7 @@ impl Function {
12341239
for diag in hir_ty::diagnostics::validate_module_item(db, krate, self.id.into()) {
12351240
sink.push(diag)
12361241
}
1242+
acc
12371243
}
12381244

12391245
/// Whether this function declaration has a definition.

crates/hir_ty/src/diagnostics/expr.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use hir_def::{
88
expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule,
99
};
1010
use hir_expand::name;
11+
use itertools::Either;
1112
use rustc_hash::FxHashSet;
1213

1314
use crate::{
@@ -26,13 +27,8 @@ pub(crate) use hir_def::{
2627
};
2728

2829
pub enum BodyValidationDiagnostic {
29-
RecordLiteralMissingFields {
30-
record_expr: ExprId,
31-
variant: VariantId,
32-
missed_fields: Vec<LocalFieldId>,
33-
},
34-
RecordPatMissingFields {
35-
record_pat: PatId,
30+
RecordMissingFields {
31+
record: Either<ExprId, PatId>,
3632
variant: VariantId,
3733
missed_fields: Vec<LocalFieldId>,
3834
},
@@ -95,8 +91,8 @@ impl ExprValidator {
9591
if let Some((variant, missed_fields, true)) =
9692
record_literal_missing_fields(db, &self.infer, id, expr)
9793
{
98-
self.diagnostics.push(BodyValidationDiagnostic::RecordLiteralMissingFields {
99-
record_expr: id,
94+
self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
95+
record: Either::Left(id),
10096
variant,
10197
missed_fields,
10298
});
@@ -116,8 +112,8 @@ impl ExprValidator {
116112
if let Some((variant, missed_fields, true)) =
117113
record_pattern_missing_fields(db, &self.infer, id, pat)
118114
{
119-
self.diagnostics.push(BodyValidationDiagnostic::RecordPatMissingFields {
120-
record_pat: id,
115+
self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
116+
record: Either::Right(id),
121117
variant,
122118
missed_fields,
123119
});

0 commit comments

Comments
 (0)