aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/diagnostics
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/diagnostics')
-rw-r--r--crates/ide/src/diagnostics/field_shorthand.rs200
1 files changed, 200 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..54e9fce9e
--- /dev/null
+++ b/crates/ide/src/diagnostics/field_shorthand.rs
@@ -0,0 +1,200 @@
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};
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(
50 Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix(
51 Some(Fix::new(
52 "Use struct shorthand initialization",
53 SourceFileEdit { file_id, edit }.into(),
54 field_range,
55 )),
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(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix(
90 Some(Fix::new(
91 "Use struct field shorthand",
92 SourceFileEdit { file_id, edit }.into(),
93 field_range,
94 )),
95 ));
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use crate::diagnostics::tests::{check_fix, check_no_diagnostics};
102
103 #[test]
104 fn test_check_expr_field_shorthand() {
105 check_no_diagnostics(
106 r#"
107struct A { a: &'static str }
108fn main() { A { a: "hello" } }
109"#,
110 );
111 check_no_diagnostics(
112 r#"
113struct A(usize);
114fn main() { A { 0: 0 } }
115"#,
116 );
117
118 check_fix(
119 r#"
120struct A { a: &'static str }
121fn main() {
122 let a = "haha";
123 A { a<|>: a }
124}
125"#,
126 r#"
127struct A { a: &'static str }
128fn main() {
129 let a = "haha";
130 A { a }
131}
132"#,
133 );
134
135 check_fix(
136 r#"
137struct A { a: &'static str, b: &'static str }
138fn main() {
139 let a = "haha";
140 let b = "bb";
141 A { a<|>: a, b }
142}
143"#,
144 r#"
145struct A { a: &'static str, b: &'static str }
146fn main() {
147 let a = "haha";
148 let b = "bb";
149 A { a, b }
150}
151"#,
152 );
153 }
154
155 #[test]
156 fn test_check_pat_field_shorthand() {
157 check_no_diagnostics(
158 r#"
159struct A { a: &'static str }
160fn f(a: A) { let A { a: hello } = a; }
161"#,
162 );
163 check_no_diagnostics(
164 r#"
165struct A(usize);
166fn f(a: A) { let A { 0: 0 } = a; }
167"#,
168 );
169
170 check_fix(
171 r#"
172struct A { a: &'static str }
173fn f(a: A) {
174 let A { a<|>: a } = a;
175}
176"#,
177 r#"
178struct A { a: &'static str }
179fn f(a: A) {
180 let A { a } = a;
181}
182"#,
183 );
184
185 check_fix(
186 r#"
187struct A { a: &'static str, b: &'static str }
188fn f(a: A) {
189 let A { a<|>: a, b } = a;
190}
191"#,
192 r#"
193struct A { a: &'static str, b: &'static str }
194fn f(a: A) {
195 let A { a, b } = a;
196}
197"#,
198 );
199 }
200}