Skip to content

Commit 7166e85

Browse files
committed
internal: refactor NoSuchField diagnostic
1 parent d3621ee commit 7166e85

File tree

5 files changed

+300
-314
lines changed

5 files changed

+300
-314
lines changed

crates/hir/src/diagnostics.rs

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ diagnostics![
3535
InactiveCode,
3636
MacroError,
3737
MissingFields,
38+
NoSuchField,
3839
UnimplementedBuiltinMacro,
3940
UnresolvedExternCrate,
4041
UnresolvedImport,
@@ -92,31 +93,9 @@ pub struct UnimplementedBuiltinMacro {
9293
pub node: InFile<SyntaxNodePtr>,
9394
}
9495

95-
// Diagnostic: no-such-field
96-
//
97-
// This diagnostic is triggered if created structure does not have field provided in record.
9896
#[derive(Debug)]
9997
pub struct NoSuchField {
100-
pub file: HirFileId,
101-
pub field: AstPtr<ast::RecordExprField>,
102-
}
103-
104-
impl Diagnostic for NoSuchField {
105-
fn code(&self) -> DiagnosticCode {
106-
DiagnosticCode("no-such-field")
107-
}
108-
109-
fn message(&self) -> String {
110-
"no such field".to_string()
111-
}
112-
113-
fn display_source(&self) -> InFile<SyntaxNodePtr> {
114-
InFile::new(self.file, self.field.clone().into())
115-
}
116-
117-
fn as_any(&self) -> &(dyn Any + Send + 'static) {
118-
self
119-
}
98+
pub field: InFile<AstPtr<ast::RecordExprField>>,
12099
}
121100

122101
// Diagnostic: break-outside-of-loop

crates/hir/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,7 @@ impl Function {
10771077
match d {
10781078
hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
10791079
let field = source_map.field_syntax(*expr);
1080-
sink.push(NoSuchField { file: field.file_id, field: field.value })
1080+
acc.push(NoSuchField { field }.into())
10811081
}
10821082
hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
10831083
let ptr = source_map

crates/ide/src/diagnostics.rs

Lines changed: 11 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44
//! macro-expanded files, but we need to present them to the users in terms of
55
//! original files. So we need to map the ranges.
66
7-
mod unresolved_module;
7+
mod inactive_code;
8+
mod macro_error;
9+
mod missing_fields;
10+
mod no_such_field;
11+
mod unimplemented_builtin_macro;
812
mod unresolved_extern_crate;
913
mod unresolved_import;
1014
mod unresolved_macro_call;
15+
mod unresolved_module;
1116
mod unresolved_proc_macro;
12-
mod unimplemented_builtin_macro;
13-
mod macro_error;
14-
mod inactive_code;
15-
mod missing_fields;
1617

1718
mod fixes;
1819
mod field_shorthand;
@@ -161,9 +162,6 @@ pub(crate) fn diagnostics(
161162
.on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| {
162163
res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
163164
})
164-
.on::<hir::diagnostics::NoSuchField, _>(|d| {
165-
res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
166-
})
167165
.on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| {
168166
res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
169167
})
@@ -220,14 +218,15 @@ pub(crate) fn diagnostics(
220218
for diag in diags {
221219
#[rustfmt::skip]
222220
let d = match diag {
223-
AnyDiagnostic::UnresolvedModule(d) => unresolved_module::unresolved_module(&ctx, &d),
221+
AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d),
222+
AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
223+
AnyDiagnostic::NoSuchField(d) => no_such_field::no_such_field(&ctx, &d),
224+
AnyDiagnostic::UnimplementedBuiltinMacro(d) => unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
224225
AnyDiagnostic::UnresolvedExternCrate(d) => unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
225226
AnyDiagnostic::UnresolvedImport(d) => unresolved_import::unresolved_import(&ctx, &d),
226227
AnyDiagnostic::UnresolvedMacroCall(d) => unresolved_macro_call::unresolved_macro_call(&ctx, &d),
228+
AnyDiagnostic::UnresolvedModule(d) => unresolved_module::unresolved_module(&ctx, &d),
227229
AnyDiagnostic::UnresolvedProcMacro(d) => unresolved_proc_macro::unresolved_proc_macro(&ctx, &d),
228-
AnyDiagnostic::UnimplementedBuiltinMacro(d) => unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
229-
AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
230-
AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d),
231230

232231
AnyDiagnostic::InactiveCode(d) => match inactive_code::inactive_code(&ctx, &d) {
233232
Some(it) => it,
@@ -722,129 +721,6 @@ fn foo() { break; }
722721
);
723722
}
724723

725-
#[test]
726-
fn no_such_field_diagnostics() {
727-
check_diagnostics(
728-
r#"
729-
struct S { foo: i32, bar: () }
730-
impl S {
731-
fn new() -> S {
732-
S {
733-
//^ Missing structure fields:
734-
//| - bar
735-
foo: 92,
736-
baz: 62,
737-
//^^^^^^^ no such field
738-
}
739-
}
740-
}
741-
"#,
742-
);
743-
}
744-
#[test]
745-
fn no_such_field_with_feature_flag_diagnostics() {
746-
check_diagnostics(
747-
r#"
748-
//- /lib.rs crate:foo cfg:feature=foo
749-
struct MyStruct {
750-
my_val: usize,
751-
#[cfg(feature = "foo")]
752-
bar: bool,
753-
}
754-
755-
impl MyStruct {
756-
#[cfg(feature = "foo")]
757-
pub(crate) fn new(my_val: usize, bar: bool) -> Self {
758-
Self { my_val, bar }
759-
}
760-
#[cfg(not(feature = "foo"))]
761-
pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
762-
Self { my_val }
763-
}
764-
}
765-
"#,
766-
);
767-
}
768-
769-
#[test]
770-
fn no_such_field_enum_with_feature_flag_diagnostics() {
771-
check_diagnostics(
772-
r#"
773-
//- /lib.rs crate:foo cfg:feature=foo
774-
enum Foo {
775-
#[cfg(not(feature = "foo"))]
776-
Buz,
777-
#[cfg(feature = "foo")]
778-
Bar,
779-
Baz
780-
}
781-
782-
fn test_fn(f: Foo) {
783-
match f {
784-
Foo::Bar => {},
785-
Foo::Baz => {},
786-
}
787-
}
788-
"#,
789-
);
790-
}
791-
792-
#[test]
793-
fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
794-
check_diagnostics(
795-
r#"
796-
//- /lib.rs crate:foo cfg:feature=foo
797-
struct S {
798-
#[cfg(feature = "foo")]
799-
foo: u32,
800-
#[cfg(not(feature = "foo"))]
801-
bar: u32,
802-
}
803-
804-
impl S {
805-
#[cfg(feature = "foo")]
806-
fn new(foo: u32) -> Self {
807-
Self { foo }
808-
}
809-
#[cfg(not(feature = "foo"))]
810-
fn new(bar: u32) -> Self {
811-
Self { bar }
812-
}
813-
fn new2(bar: u32) -> Self {
814-
#[cfg(feature = "foo")]
815-
{ Self { foo: bar } }
816-
#[cfg(not(feature = "foo"))]
817-
{ Self { bar } }
818-
}
819-
fn new2(val: u32) -> Self {
820-
Self {
821-
#[cfg(feature = "foo")]
822-
foo: val,
823-
#[cfg(not(feature = "foo"))]
824-
bar: val,
825-
}
826-
}
827-
}
828-
"#,
829-
);
830-
}
831-
832-
#[test]
833-
fn no_such_field_with_type_macro() {
834-
check_diagnostics(
835-
r#"
836-
macro_rules! Type { () => { u32 }; }
837-
struct Foo { bar: Type![] }
838-
839-
impl Foo {
840-
fn new() -> Self {
841-
Foo { bar: 0 }
842-
}
843-
}
844-
"#,
845-
);
846-
}
847-
848724
#[test]
849725
fn missing_unsafe_diagnostic_with_raw_ptr() {
850726
check_diagnostics(
Lines changed: 0 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,156 +1 @@
1-
use hir::{db::AstDatabase, diagnostics::NoSuchField, HasSource, HirDisplay, Semantics};
2-
use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase};
3-
use syntax::{
4-
ast::{self, edit::IndentLevel, make},
5-
AstNode,
6-
};
7-
use text_edit::TextEdit;
81

9-
use crate::{
10-
diagnostics::{fix, DiagnosticWithFixes},
11-
Assist, AssistResolveStrategy,
12-
};
13-
impl DiagnosticWithFixes for NoSuchField {
14-
fn fixes(
15-
&self,
16-
sema: &Semantics<RootDatabase>,
17-
_resolve: &AssistResolveStrategy,
18-
) -> Option<Vec<Assist>> {
19-
let root = sema.db.parse_or_expand(self.file)?;
20-
missing_record_expr_field_fixes(
21-
sema,
22-
self.file.original_file(sema.db),
23-
&self.field.to_node(&root),
24-
)
25-
}
26-
}
27-
28-
fn missing_record_expr_field_fixes(
29-
sema: &Semantics<RootDatabase>,
30-
usage_file_id: FileId,
31-
record_expr_field: &ast::RecordExprField,
32-
) -> Option<Vec<Assist>> {
33-
let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?;
34-
let def_id = sema.resolve_variant(record_lit)?;
35-
let module;
36-
let def_file_id;
37-
let record_fields = match def_id {
38-
hir::VariantDef::Struct(s) => {
39-
module = s.module(sema.db);
40-
let source = s.source(sema.db)?;
41-
def_file_id = source.file_id;
42-
let fields = source.value.field_list()?;
43-
record_field_list(fields)?
44-
}
45-
hir::VariantDef::Union(u) => {
46-
module = u.module(sema.db);
47-
let source = u.source(sema.db)?;
48-
def_file_id = source.file_id;
49-
source.value.record_field_list()?
50-
}
51-
hir::VariantDef::Variant(e) => {
52-
module = e.module(sema.db);
53-
let source = e.source(sema.db)?;
54-
def_file_id = source.file_id;
55-
let fields = source.value.field_list()?;
56-
record_field_list(fields)?
57-
}
58-
};
59-
let def_file_id = def_file_id.original_file(sema.db);
60-
61-
let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?;
62-
if new_field_type.is_unknown() {
63-
return None;
64-
}
65-
let new_field = make::record_field(
66-
None,
67-
make::name(&record_expr_field.field_name()?.text()),
68-
make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
69-
);
70-
71-
let last_field = record_fields.fields().last()?;
72-
let last_field_syntax = last_field.syntax();
73-
let indent = IndentLevel::from_node(last_field_syntax);
74-
75-
let mut new_field = new_field.to_string();
76-
if usage_file_id != def_file_id {
77-
new_field = format!("pub(crate) {}", new_field);
78-
}
79-
new_field = format!("\n{}{}", indent, new_field);
80-
81-
let needs_comma = !last_field_syntax.to_string().ends_with(',');
82-
if needs_comma {
83-
new_field = format!(",{}", new_field);
84-
}
85-
86-
let source_change = SourceChange::from_text_edit(
87-
def_file_id,
88-
TextEdit::insert(last_field_syntax.text_range().end(), new_field),
89-
);
90-
91-
return Some(vec![fix(
92-
"create_field",
93-
"Create field",
94-
source_change,
95-
record_expr_field.syntax().text_range(),
96-
)]);
97-
98-
fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> {
99-
match field_def_list {
100-
ast::FieldList::RecordFieldList(it) => Some(it),
101-
ast::FieldList::TupleFieldList(_) => None,
102-
}
103-
}
104-
}
105-
106-
#[cfg(test)]
107-
mod tests {
108-
use crate::diagnostics::tests::check_fix;
109-
110-
#[test]
111-
fn test_add_field_from_usage() {
112-
check_fix(
113-
r"
114-
fn main() {
115-
Foo { bar: 3, baz$0: false};
116-
}
117-
struct Foo {
118-
bar: i32
119-
}
120-
",
121-
r"
122-
fn main() {
123-
Foo { bar: 3, baz: false};
124-
}
125-
struct Foo {
126-
bar: i32,
127-
baz: bool
128-
}
129-
",
130-
)
131-
}
132-
133-
#[test]
134-
fn test_add_field_in_other_file_from_usage() {
135-
check_fix(
136-
r#"
137-
//- /main.rs
138-
mod foo;
139-
140-
fn main() {
141-
foo::Foo { bar: 3, $0baz: false};
142-
}
143-
//- /foo.rs
144-
struct Foo {
145-
bar: i32
146-
}
147-
"#,
148-
r#"
149-
struct Foo {
150-
bar: i32,
151-
pub(crate) baz: bool
152-
}
153-
"#,
154-
)
155-
}
156-
}

0 commit comments

Comments
 (0)