diff options
Diffstat (limited to 'crates/ide/src/diagnostics/field_shorthand.rs')
-rw-r--r-- | crates/ide/src/diagnostics/field_shorthand.rs | 206 |
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 | |||
4 | use base_db::FileId; | ||
5 | use ide_db::source_change::SourceFileEdit; | ||
6 | use syntax::{ast, match_ast, AstNode, SyntaxNode}; | ||
7 | use text_edit::TextEdit; | ||
8 | |||
9 | use crate::{Diagnostic, Fix, Severity}; | ||
10 | |||
11 | pub(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 | |||
21 | fn 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 | |||
63 | fn 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)] | ||
106 | mod 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#" | ||
113 | struct A { a: &'static str } | ||
114 | fn main() { A { a: "hello" } } | ||
115 | "#, | ||
116 | ); | ||
117 | check_no_diagnostics( | ||
118 | r#" | ||
119 | struct A(usize); | ||
120 | fn main() { A { 0: 0 } } | ||
121 | "#, | ||
122 | ); | ||
123 | |||
124 | check_fix( | ||
125 | r#" | ||
126 | struct A { a: &'static str } | ||
127 | fn main() { | ||
128 | let a = "haha"; | ||
129 | A { a<|>: a } | ||
130 | } | ||
131 | "#, | ||
132 | r#" | ||
133 | struct A { a: &'static str } | ||
134 | fn main() { | ||
135 | let a = "haha"; | ||
136 | A { a } | ||
137 | } | ||
138 | "#, | ||
139 | ); | ||
140 | |||
141 | check_fix( | ||
142 | r#" | ||
143 | struct A { a: &'static str, b: &'static str } | ||
144 | fn main() { | ||
145 | let a = "haha"; | ||
146 | let b = "bb"; | ||
147 | A { a<|>: a, b } | ||
148 | } | ||
149 | "#, | ||
150 | r#" | ||
151 | struct A { a: &'static str, b: &'static str } | ||
152 | fn 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#" | ||
165 | struct A { a: &'static str } | ||
166 | fn f(a: A) { let A { a: hello } = a; } | ||
167 | "#, | ||
168 | ); | ||
169 | check_no_diagnostics( | ||
170 | r#" | ||
171 | struct A(usize); | ||
172 | fn f(a: A) { let A { 0: 0 } = a; } | ||
173 | "#, | ||
174 | ); | ||
175 | |||
176 | check_fix( | ||
177 | r#" | ||
178 | struct A { a: &'static str } | ||
179 | fn f(a: A) { | ||
180 | let A { a<|>: a } = a; | ||
181 | } | ||
182 | "#, | ||
183 | r#" | ||
184 | struct A { a: &'static str } | ||
185 | fn f(a: A) { | ||
186 | let A { a } = a; | ||
187 | } | ||
188 | "#, | ||
189 | ); | ||
190 | |||
191 | check_fix( | ||
192 | r#" | ||
193 | struct A { a: &'static str, b: &'static str } | ||
194 | fn f(a: A) { | ||
195 | let A { a<|>: a, b } = a; | ||
196 | } | ||
197 | "#, | ||
198 | r#" | ||
199 | struct A { a: &'static str, b: &'static str } | ||
200 | fn f(a: A) { | ||
201 | let A { a, b } = a; | ||
202 | } | ||
203 | "#, | ||
204 | ); | ||
205 | } | ||
206 | } | ||