aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/diagnostics/missing_fields.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-06-13 13:27:15 +0100
committerAleksey Kladov <[email protected]>2021-06-13 13:27:15 +0100
commitc6509a4592b67acc4a99a7ffd6dd688bc6cd29be (patch)
treed174244c1e35cc91a98d1aced254a560079e48c5 /crates/ide/src/diagnostics/missing_fields.rs
parentefa069d28818dd074afd2c7cee776907b63ca012 (diff)
internal: move missing_fields diagnostics
Diffstat (limited to 'crates/ide/src/diagnostics/missing_fields.rs')
-rw-r--r--crates/ide/src/diagnostics/missing_fields.rs241
1 files changed, 241 insertions, 0 deletions
diff --git a/crates/ide/src/diagnostics/missing_fields.rs b/crates/ide/src/diagnostics/missing_fields.rs
new file mode 100644
index 000000000..aee780972
--- /dev/null
+++ b/crates/ide/src/diagnostics/missing_fields.rs
@@ -0,0 +1,241 @@
1use hir::{db::AstDatabase, InFile};
2use ide_assists::Assist;
3use ide_db::source_change::SourceChange;
4use stdx::format_to;
5use syntax::{algo, ast::make, AstNode, SyntaxNodePtr};
6use text_edit::TextEdit;
7
8use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext};
9
10// Diagnostic: missing-structure-fields
11//
12// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
13//
14// Example:
15//
16// ```rust
17// struct A { a: u8, b: u8 }
18//
19// let a = A { a: 10 };
20// ```
21pub(super) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic {
22 let mut message = String::from("Missing structure fields:\n");
23 for field in &d.missed_fields {
24 format_to!(message, "- {}\n", field);
25 }
26
27 let ptr = InFile::new(
28 d.file,
29 d.field_list_parent_path
30 .clone()
31 .map(SyntaxNodePtr::from)
32 .unwrap_or_else(|| d.field_list_parent.clone().into()),
33 );
34
35 Diagnostic::new(
36 "missing-structure-fields",
37 message,
38 ctx.sema.diagnostics_display_range(ptr).range,
39 )
40 .with_fixes(fixes(ctx, d))
41}
42
43fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Assist>> {
44 // Note that although we could add a diagnostics to
45 // fill the missing tuple field, e.g :
46 // `struct A(usize);`
47 // `let a = A { 0: () }`
48 // but it is uncommon usage and it should not be encouraged.
49 if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
50 return None;
51 }
52
53 let root = ctx.sema.db.parse_or_expand(d.file)?;
54 let field_list_parent = d.field_list_parent.to_node(&root);
55 let old_field_list = field_list_parent.record_expr_field_list()?;
56 let new_field_list = old_field_list.clone_for_update();
57 for f in d.missed_fields.iter() {
58 let field =
59 make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()))
60 .clone_for_update();
61 new_field_list.add_field(field);
62 }
63
64 let edit = {
65 let mut builder = TextEdit::builder();
66 algo::diff(old_field_list.syntax(), new_field_list.syntax()).into_text_edit(&mut builder);
67 builder.finish()
68 };
69 Some(vec![fix(
70 "fill_missing_fields",
71 "Fill struct fields",
72 SourceChange::from_text_edit(d.file.original_file(ctx.sema.db), edit),
73 ctx.sema.original_range(field_list_parent.syntax()).range,
74 )])
75}
76
77#[cfg(test)]
78mod tests {
79 use crate::diagnostics::tests::{check_fix, check_no_diagnostics};
80
81 #[test]
82 fn test_fill_struct_fields_empty() {
83 check_fix(
84 r#"
85struct TestStruct { one: i32, two: i64 }
86
87fn test_fn() {
88 let s = TestStruct {$0};
89}
90"#,
91 r#"
92struct TestStruct { one: i32, two: i64 }
93
94fn test_fn() {
95 let s = TestStruct { one: (), two: () };
96}
97"#,
98 );
99 }
100
101 #[test]
102 fn test_fill_struct_fields_self() {
103 check_fix(
104 r#"
105struct TestStruct { one: i32 }
106
107impl TestStruct {
108 fn test_fn() { let s = Self {$0}; }
109}
110"#,
111 r#"
112struct TestStruct { one: i32 }
113
114impl TestStruct {
115 fn test_fn() { let s = Self { one: () }; }
116}
117"#,
118 );
119 }
120
121 #[test]
122 fn test_fill_struct_fields_enum() {
123 check_fix(
124 r#"
125enum Expr {
126 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
127}
128
129impl Expr {
130 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
131 Expr::Bin {$0 }
132 }
133}
134"#,
135 r#"
136enum Expr {
137 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
138}
139
140impl Expr {
141 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
142 Expr::Bin { lhs: (), rhs: () }
143 }
144}
145"#,
146 );
147 }
148
149 #[test]
150 fn test_fill_struct_fields_partial() {
151 check_fix(
152 r#"
153struct TestStruct { one: i32, two: i64 }
154
155fn test_fn() {
156 let s = TestStruct{ two: 2$0 };
157}
158"#,
159 r"
160struct TestStruct { one: i32, two: i64 }
161
162fn test_fn() {
163 let s = TestStruct{ two: 2, one: () };
164}
165",
166 );
167 }
168
169 #[test]
170 fn test_fill_struct_fields_raw_ident() {
171 check_fix(
172 r#"
173struct TestStruct { r#type: u8 }
174
175fn test_fn() {
176 TestStruct { $0 };
177}
178"#,
179 r"
180struct TestStruct { r#type: u8 }
181
182fn test_fn() {
183 TestStruct { r#type: () };
184}
185",
186 );
187 }
188
189 #[test]
190 fn test_fill_struct_fields_no_diagnostic() {
191 check_no_diagnostics(
192 r#"
193struct TestStruct { one: i32, two: i64 }
194
195fn test_fn() {
196 let one = 1;
197 let s = TestStruct{ one, two: 2 };
198}
199 "#,
200 );
201 }
202
203 #[test]
204 fn test_fill_struct_fields_no_diagnostic_on_spread() {
205 check_no_diagnostics(
206 r#"
207struct TestStruct { one: i32, two: i64 }
208
209fn test_fn() {
210 let one = 1;
211 let s = TestStruct{ ..a };
212}
213"#,
214 );
215 }
216
217 #[test]
218 fn test_fill_struct_fields_blank_line() {
219 check_fix(
220 r#"
221struct S { a: (), b: () }
222
223fn f() {
224 S {
225 $0
226 };
227}
228"#,
229 r#"
230struct S { a: (), b: () }
231
232fn f() {
233 S {
234 a: (),
235 b: (),
236 };
237}
238"#,
239 );
240 }
241}