diff options
author | Aleksey Kladov <[email protected]> | 2020-10-15 16:07:53 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-10-15 16:10:06 +0100 |
commit | 3086fb2ef4064ab7daa8194a8d57c742849ca900 (patch) | |
tree | bc5cb9aa891a7036c920a0f60c2d5f9c24ec0135 | |
parent | 1022672af0467edf480f50398989c3f8016ccd10 (diff) |
Move field_shorthand to a separate module
-rw-r--r-- | crates/ide/src/diagnostics.rs | 201 | ||||
-rw-r--r-- | crates/ide/src/diagnostics/field_shorthand.rs | 206 |
2 files changed, 211 insertions, 196 deletions
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index c36c321f2..1e5ea4617 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -5,6 +5,7 @@ | |||
5 | //! original files. So we need to map the ranges. | 5 | //! original files. So we need to map the ranges. |
6 | 6 | ||
7 | mod fixes; | 7 | mod fixes; |
8 | mod field_shorthand; | ||
8 | 9 | ||
9 | use std::cell::RefCell; | 10 | use std::cell::RefCell; |
10 | 11 | ||
@@ -15,7 +16,7 @@ use itertools::Itertools; | |||
15 | use rustc_hash::FxHashSet; | 16 | use rustc_hash::FxHashSet; |
16 | use syntax::{ | 17 | use syntax::{ |
17 | ast::{self, AstNode}, | 18 | ast::{self, AstNode}, |
18 | match_ast, SyntaxNode, TextRange, T, | 19 | SyntaxNode, TextRange, T, |
19 | }; | 20 | }; |
20 | use text_edit::TextEdit; | 21 | use text_edit::TextEdit; |
21 | 22 | ||
@@ -80,7 +81,7 @@ pub(crate) fn diagnostics( | |||
80 | 81 | ||
81 | for node in parse.tree().syntax().descendants() { | 82 | for node in parse.tree().syntax().descendants() { |
82 | check_unnecessary_braces_in_use_statement(&mut res, file_id, &node); | 83 | check_unnecessary_braces_in_use_statement(&mut res, file_id, &node); |
83 | check_field_shorthand(&mut res, file_id, &node); | 84 | field_shorthand::check(&mut res, file_id, &node); |
84 | } | 85 | } |
85 | let res = RefCell::new(res); | 86 | let res = RefCell::new(res); |
86 | let sink_builder = DiagnosticSinkBuilder::new() | 87 | let sink_builder = DiagnosticSinkBuilder::new() |
@@ -188,100 +189,6 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement( | |||
188 | None | 189 | None |
189 | } | 190 | } |
190 | 191 | ||
191 | fn check_field_shorthand(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { | ||
192 | match_ast! { | ||
193 | match node { | ||
194 | ast::RecordExpr(it) => check_expr_field_shorthand(acc, file_id, it), | ||
195 | ast::RecordPat(it) => check_pat_field_shorthand(acc, file_id, it), | ||
196 | _ => () | ||
197 | } | ||
198 | }; | ||
199 | } | ||
200 | |||
201 | fn check_expr_field_shorthand( | ||
202 | acc: &mut Vec<Diagnostic>, | ||
203 | file_id: FileId, | ||
204 | record_expr: ast::RecordExpr, | ||
205 | ) { | ||
206 | let record_field_list = match record_expr.record_expr_field_list() { | ||
207 | Some(it) => it, | ||
208 | None => return, | ||
209 | }; | ||
210 | for record_field in record_field_list.fields() { | ||
211 | let (name_ref, expr) = match record_field.name_ref().zip(record_field.expr()) { | ||
212 | Some(it) => it, | ||
213 | None => continue, | ||
214 | }; | ||
215 | |||
216 | let field_name = name_ref.syntax().text().to_string(); | ||
217 | let field_expr = expr.syntax().text().to_string(); | ||
218 | let field_name_is_tup_index = name_ref.as_tuple_field().is_some(); | ||
219 | if field_name != field_expr || field_name_is_tup_index { | ||
220 | continue; | ||
221 | } | ||
222 | |||
223 | let mut edit_builder = TextEdit::builder(); | ||
224 | edit_builder.delete(record_field.syntax().text_range()); | ||
225 | edit_builder.insert(record_field.syntax().text_range().start(), field_name); | ||
226 | let edit = edit_builder.finish(); | ||
227 | |||
228 | let field_range = record_field.syntax().text_range(); | ||
229 | acc.push(Diagnostic { | ||
230 | // name: None, | ||
231 | range: field_range, | ||
232 | message: "Shorthand struct initialization".to_string(), | ||
233 | severity: Severity::WeakWarning, | ||
234 | fix: Some(Fix::new( | ||
235 | "Use struct shorthand initialization", | ||
236 | SourceFileEdit { file_id, edit }.into(), | ||
237 | field_range, | ||
238 | )), | ||
239 | }); | ||
240 | } | ||
241 | } | ||
242 | |||
243 | fn check_pat_field_shorthand( | ||
244 | acc: &mut Vec<Diagnostic>, | ||
245 | file_id: FileId, | ||
246 | record_pat: ast::RecordPat, | ||
247 | ) { | ||
248 | let record_pat_field_list = match record_pat.record_pat_field_list() { | ||
249 | Some(it) => it, | ||
250 | None => return, | ||
251 | }; | ||
252 | for record_pat_field in record_pat_field_list.fields() { | ||
253 | let (name_ref, pat) = match record_pat_field.name_ref().zip(record_pat_field.pat()) { | ||
254 | Some(it) => it, | ||
255 | None => continue, | ||
256 | }; | ||
257 | |||
258 | let field_name = name_ref.syntax().text().to_string(); | ||
259 | let field_pat = pat.syntax().text().to_string(); | ||
260 | let field_name_is_tup_index = name_ref.as_tuple_field().is_some(); | ||
261 | if field_name != field_pat || field_name_is_tup_index { | ||
262 | continue; | ||
263 | } | ||
264 | |||
265 | let mut edit_builder = TextEdit::builder(); | ||
266 | edit_builder.delete(record_pat_field.syntax().text_range()); | ||
267 | edit_builder.insert(record_pat_field.syntax().text_range().start(), field_name); | ||
268 | let edit = edit_builder.finish(); | ||
269 | |||
270 | let field_range = record_pat_field.syntax().text_range(); | ||
271 | acc.push(Diagnostic { | ||
272 | // name: None, | ||
273 | range: field_range, | ||
274 | message: "Shorthand struct pattern".to_string(), | ||
275 | severity: Severity::WeakWarning, | ||
276 | fix: Some(Fix::new( | ||
277 | "Use struct field shorthand", | ||
278 | SourceFileEdit { file_id, edit }.into(), | ||
279 | field_range, | ||
280 | )), | ||
281 | }); | ||
282 | } | ||
283 | } | ||
284 | |||
285 | #[cfg(test)] | 192 | #[cfg(test)] |
286 | mod tests { | 193 | mod tests { |
287 | use expect_test::{expect, Expect}; | 194 | use expect_test::{expect, Expect}; |
@@ -295,7 +202,7 @@ mod tests { | |||
295 | /// * a diagnostic is produced | 202 | /// * a diagnostic is produced |
296 | /// * this diagnostic fix trigger range touches the input cursor position | 203 | /// * this diagnostic fix trigger range touches the input cursor position |
297 | /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied | 204 | /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied |
298 | fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { | 205 | pub(super) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { |
299 | let after = trim_indent(ra_fixture_after); | 206 | let after = trim_indent(ra_fixture_after); |
300 | 207 | ||
301 | let (analysis, file_position) = fixture::position(ra_fixture_before); | 208 | let (analysis, file_position) = fixture::position(ra_fixture_before); |
@@ -377,7 +284,7 @@ mod tests { | |||
377 | 284 | ||
378 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics | 285 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics |
379 | /// apply to the file containing the cursor. | 286 | /// apply to the file containing the cursor. |
380 | fn check_no_diagnostics(ra_fixture: &str) { | 287 | pub(crate) fn check_no_diagnostics(ra_fixture: &str) { |
381 | let (analysis, files) = fixture::files(ra_fixture); | 288 | let (analysis, files) = fixture::files(ra_fixture); |
382 | let diagnostics = files | 289 | let diagnostics = files |
383 | .into_iter() | 290 | .into_iter() |
@@ -778,104 +685,6 @@ mod a { | |||
778 | } | 685 | } |
779 | 686 | ||
780 | #[test] | 687 | #[test] |
781 | fn test_check_expr_field_shorthand() { | ||
782 | check_no_diagnostics( | ||
783 | r#" | ||
784 | struct A { a: &'static str } | ||
785 | fn main() { A { a: "hello" } } | ||
786 | "#, | ||
787 | ); | ||
788 | check_no_diagnostics( | ||
789 | r#" | ||
790 | struct A(usize); | ||
791 | fn main() { A { 0: 0 } } | ||
792 | "#, | ||
793 | ); | ||
794 | |||
795 | check_fix( | ||
796 | r#" | ||
797 | struct A { a: &'static str } | ||
798 | fn main() { | ||
799 | let a = "haha"; | ||
800 | A { a<|>: a } | ||
801 | } | ||
802 | "#, | ||
803 | r#" | ||
804 | struct A { a: &'static str } | ||
805 | fn main() { | ||
806 | let a = "haha"; | ||
807 | A { a } | ||
808 | } | ||
809 | "#, | ||
810 | ); | ||
811 | |||
812 | check_fix( | ||
813 | r#" | ||
814 | struct A { a: &'static str, b: &'static str } | ||
815 | fn main() { | ||
816 | let a = "haha"; | ||
817 | let b = "bb"; | ||
818 | A { a<|>: a, b } | ||
819 | } | ||
820 | "#, | ||
821 | r#" | ||
822 | struct A { a: &'static str, b: &'static str } | ||
823 | fn main() { | ||
824 | let a = "haha"; | ||
825 | let b = "bb"; | ||
826 | A { a, b } | ||
827 | } | ||
828 | "#, | ||
829 | ); | ||
830 | } | ||
831 | |||
832 | #[test] | ||
833 | fn test_check_pat_field_shorthand() { | ||
834 | check_no_diagnostics( | ||
835 | r#" | ||
836 | struct A { a: &'static str } | ||
837 | fn f(a: A) { let A { a: hello } = a; } | ||
838 | "#, | ||
839 | ); | ||
840 | check_no_diagnostics( | ||
841 | r#" | ||
842 | struct A(usize); | ||
843 | fn f(a: A) { let A { 0: 0 } = a; } | ||
844 | "#, | ||
845 | ); | ||
846 | |||
847 | check_fix( | ||
848 | r#" | ||
849 | struct A { a: &'static str } | ||
850 | fn f(a: A) { | ||
851 | let A { a<|>: a } = a; | ||
852 | } | ||
853 | "#, | ||
854 | r#" | ||
855 | struct A { a: &'static str } | ||
856 | fn f(a: A) { | ||
857 | let A { a } = a; | ||
858 | } | ||
859 | "#, | ||
860 | ); | ||
861 | |||
862 | check_fix( | ||
863 | r#" | ||
864 | struct A { a: &'static str, b: &'static str } | ||
865 | fn f(a: A) { | ||
866 | let A { a<|>: a, b } = a; | ||
867 | } | ||
868 | "#, | ||
869 | r#" | ||
870 | struct A { a: &'static str, b: &'static str } | ||
871 | fn f(a: A) { | ||
872 | let A { a, b } = a; | ||
873 | } | ||
874 | "#, | ||
875 | ); | ||
876 | } | ||
877 | |||
878 | #[test] | ||
879 | fn test_add_field_from_usage() { | 688 | fn test_add_field_from_usage() { |
880 | check_fix( | 689 | check_fix( |
881 | r" | 690 | r" |
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 | } | ||