aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-10-15 16:07:53 +0100
committerAleksey Kladov <[email protected]>2020-10-15 16:10:06 +0100
commit3086fb2ef4064ab7daa8194a8d57c742849ca900 (patch)
treebc5cb9aa891a7036c920a0f60c2d5f9c24ec0135 /crates
parent1022672af0467edf480f50398989c3f8016ccd10 (diff)
Move field_shorthand to a separate module
Diffstat (limited to 'crates')
-rw-r--r--crates/ide/src/diagnostics.rs201
-rw-r--r--crates/ide/src/diagnostics/field_shorthand.rs206
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
7mod fixes; 7mod fixes;
8mod field_shorthand;
8 9
9use std::cell::RefCell; 10use std::cell::RefCell;
10 11
@@ -15,7 +16,7 @@ use itertools::Itertools;
15use rustc_hash::FxHashSet; 16use rustc_hash::FxHashSet;
16use syntax::{ 17use syntax::{
17 ast::{self, AstNode}, 18 ast::{self, AstNode},
18 match_ast, SyntaxNode, TextRange, T, 19 SyntaxNode, TextRange, T,
19}; 20};
20use text_edit::TextEdit; 21use 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
191fn 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
201fn 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
243fn 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)]
286mod tests { 193mod 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#"
784struct A { a: &'static str }
785fn main() { A { a: "hello" } }
786"#,
787 );
788 check_no_diagnostics(
789 r#"
790struct A(usize);
791fn main() { A { 0: 0 } }
792"#,
793 );
794
795 check_fix(
796 r#"
797struct A { a: &'static str }
798fn main() {
799 let a = "haha";
800 A { a<|>: a }
801}
802"#,
803 r#"
804struct A { a: &'static str }
805fn main() {
806 let a = "haha";
807 A { a }
808}
809"#,
810 );
811
812 check_fix(
813 r#"
814struct A { a: &'static str, b: &'static str }
815fn main() {
816 let a = "haha";
817 let b = "bb";
818 A { a<|>: a, b }
819}
820"#,
821 r#"
822struct A { a: &'static str, b: &'static str }
823fn 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#"
836struct A { a: &'static str }
837fn f(a: A) { let A { a: hello } = a; }
838"#,
839 );
840 check_no_diagnostics(
841 r#"
842struct A(usize);
843fn f(a: A) { let A { 0: 0 } = a; }
844"#,
845 );
846
847 check_fix(
848 r#"
849struct A { a: &'static str }
850fn f(a: A) {
851 let A { a<|>: a } = a;
852}
853"#,
854 r#"
855struct A { a: &'static str }
856fn f(a: A) {
857 let A { a } = a;
858}
859"#,
860 );
861
862 check_fix(
863 r#"
864struct A { a: &'static str, b: &'static str }
865fn f(a: A) {
866 let A { a<|>: a, b } = a;
867}
868"#,
869 r#"
870struct A { a: &'static str, b: &'static str }
871fn 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
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}