aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_diagnostics/src/field_shorthand.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_diagnostics/src/field_shorthand.rs')
-rw-r--r--crates/ide_diagnostics/src/field_shorthand.rs203
1 files changed, 203 insertions, 0 deletions
diff --git a/crates/ide_diagnostics/src/field_shorthand.rs b/crates/ide_diagnostics/src/field_shorthand.rs
new file mode 100644
index 000000000..0b6af9965
--- /dev/null
+++ b/crates/ide_diagnostics/src/field_shorthand.rs
@@ -0,0 +1,203 @@
1//! Suggests shortening `Foo { field: field }` to `Foo { field }` in both
2//! expressions and patterns.
3
4use ide_db::{base_db::FileId, source_change::SourceChange};
5use syntax::{ast, match_ast, AstNode, SyntaxNode};
6use text_edit::TextEdit;
7
8use crate::{fix, Diagnostic, Severity};
9
10pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) {
11 match_ast! {
12 match node {
13 ast::RecordExpr(it) => check_expr_field_shorthand(acc, file_id, it),
14 ast::RecordPat(it) => check_pat_field_shorthand(acc, file_id, it),
15 _ => ()
16 }
17 };
18}
19
20fn check_expr_field_shorthand(
21 acc: &mut Vec<Diagnostic>,
22 file_id: FileId,
23 record_expr: ast::RecordExpr,
24) {
25 let record_field_list = match record_expr.record_expr_field_list() {
26 Some(it) => it,
27 None => return,
28 };
29 for record_field in record_field_list.fields() {
30 let (name_ref, expr) = match record_field.name_ref().zip(record_field.expr()) {
31 Some(it) => it,
32 None => continue,
33 };
34
35 let field_name = name_ref.syntax().text().to_string();
36 let field_expr = expr.syntax().text().to_string();
37 let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
38 if field_name != field_expr || field_name_is_tup_index {
39 continue;
40 }
41
42 let mut edit_builder = TextEdit::builder();
43 edit_builder.delete(record_field.syntax().text_range());
44 edit_builder.insert(record_field.syntax().text_range().start(), field_name);
45 let edit = edit_builder.finish();
46
47 let field_range = record_field.syntax().text_range();
48 acc.push(
49 Diagnostic::new("use-field-shorthand", "Shorthand struct initialization", field_range)
50 .severity(Severity::WeakWarning)
51 .with_fixes(Some(vec![fix(
52 "use_expr_field_shorthand",
53 "Use struct shorthand initialization",
54 SourceChange::from_text_edit(file_id, edit),
55 field_range,
56 )])),
57 );
58 }
59}
60
61fn check_pat_field_shorthand(
62 acc: &mut Vec<Diagnostic>,
63 file_id: FileId,
64 record_pat: ast::RecordPat,
65) {
66 let record_pat_field_list = match record_pat.record_pat_field_list() {
67 Some(it) => it,
68 None => return,
69 };
70 for record_pat_field in record_pat_field_list.fields() {
71 let (name_ref, pat) = match record_pat_field.name_ref().zip(record_pat_field.pat()) {
72 Some(it) => it,
73 None => continue,
74 };
75
76 let field_name = name_ref.syntax().text().to_string();
77 let field_pat = pat.syntax().text().to_string();
78 let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
79 if field_name != field_pat || field_name_is_tup_index {
80 continue;
81 }
82
83 let mut edit_builder = TextEdit::builder();
84 edit_builder.delete(record_pat_field.syntax().text_range());
85 edit_builder.insert(record_pat_field.syntax().text_range().start(), field_name);
86 let edit = edit_builder.finish();
87
88 let field_range = record_pat_field.syntax().text_range();
89 acc.push(
90 Diagnostic::new("use-field-shorthand", "Shorthand struct pattern", field_range)
91 .severity(Severity::WeakWarning)
92 .with_fixes(Some(vec![fix(
93 "use_pat_field_shorthand",
94 "Use struct field shorthand",
95 SourceChange::from_text_edit(file_id, edit),
96 field_range,
97 )])),
98 );
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use crate::tests::{check_diagnostics, check_fix};
105
106 #[test]
107 fn test_check_expr_field_shorthand() {
108 check_diagnostics(
109 r#"
110struct A { a: &'static str }
111fn main() { A { a: "hello" } }
112"#,
113 );
114 check_diagnostics(
115 r#"
116struct A(usize);
117fn main() { A { 0: 0 } }
118"#,
119 );
120
121 check_fix(
122 r#"
123struct A { a: &'static str }
124fn main() {
125 let a = "haha";
126 A { a$0: a }
127}
128"#,
129 r#"
130struct A { a: &'static str }
131fn main() {
132 let a = "haha";
133 A { a }
134}
135"#,
136 );
137
138 check_fix(
139 r#"
140struct A { a: &'static str, b: &'static str }
141fn main() {
142 let a = "haha";
143 let b = "bb";
144 A { a$0: a, b }
145}
146"#,
147 r#"
148struct A { a: &'static str, b: &'static str }
149fn main() {
150 let a = "haha";
151 let b = "bb";
152 A { a, b }
153}
154"#,
155 );
156 }
157
158 #[test]
159 fn test_check_pat_field_shorthand() {
160 check_diagnostics(
161 r#"
162struct A { a: &'static str }
163fn f(a: A) { let A { a: hello } = a; }
164"#,
165 );
166 check_diagnostics(
167 r#"
168struct A(usize);
169fn f(a: A) { let A { 0: 0 } = a; }
170"#,
171 );
172
173 check_fix(
174 r#"
175struct A { a: &'static str }
176fn f(a: A) {
177 let A { a$0: a } = a;
178}
179"#,
180 r#"
181struct A { a: &'static str }
182fn f(a: A) {
183 let A { a } = a;
184}
185"#,
186 );
187
188 check_fix(
189 r#"
190struct A { a: &'static str, b: &'static str }
191fn f(a: A) {
192 let A { a$0: a, b } = a;
193}
194"#,
195 r#"
196struct A { a: &'static str, b: &'static str }
197fn f(a: A) {
198 let A { a, b } = a;
199}
200"#,
201 );
202 }
203}