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