]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/record.rs
Merge #9972
[rust.git] / crates / ide_completion / src / completions / record.rs
1 //! Complete fields in record literals and patterns.
2 use ide_db::{helpers::FamousDefs, SymbolKind};
3 use syntax::{ast::Expr, T};
4
5 use crate::{
6     item::CompletionKind, patterns::ImmediateLocation, CompletionContext, CompletionItem,
7     CompletionItemKind, Completions,
8 };
9
10 pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
11     let missing_fields = match &ctx.completion_location {
12         Some(
13             ImmediateLocation::RecordExpr(record_expr)
14             | ImmediateLocation::RecordExprUpdate(record_expr),
15         ) => {
16             let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
17             let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default();
18             let impl_default_trait = default_trait.zip(ty).map_or(false, |(default_trait, ty)| {
19                 ty.original.impls_trait(ctx.db, default_trait, &[])
20             });
21
22             let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
23             if impl_default_trait && !missing_fields.is_empty() && ctx.path_qual().is_none() {
24                 let completion_text = "..Default::default()";
25                 let mut item = CompletionItem::new(
26                     CompletionKind::Snippet,
27                     ctx.source_range(),
28                     completion_text,
29                 );
30                 let completion_text =
31                     completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text);
32                 item.insert_text(completion_text).kind(SymbolKind::Field);
33                 item.add_to(acc);
34             }
35             if ctx.previous_token_is(T![.]) {
36                 let mut item =
37                     CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), "..");
38                 item.insert_text(".").kind(CompletionItemKind::Snippet);
39                 item.add_to(acc);
40                 return None;
41             }
42             missing_fields
43         }
44         Some(ImmediateLocation::RecordPat(record_pat)) => {
45             ctx.sema.record_pattern_missing_fields(record_pat)
46         }
47         _ => return None,
48     };
49
50     for (field, ty) in missing_fields {
51         acc.add_field(ctx, None, field, &ty);
52     }
53
54     Some(())
55 }
56
57 pub(crate) fn complete_record_literal(
58     acc: &mut Completions,
59     ctx: &CompletionContext,
60 ) -> Option<()> {
61     if !ctx.expects_expression() {
62         return None;
63     }
64
65     if let hir::Adt::Struct(strukt) = ctx.expected_type.as_ref()?.as_adt()? {
66         acc.add_struct_literal(ctx, strukt, None);
67     }
68
69     Some(())
70 }
71
72 #[cfg(test)]
73 mod tests {
74     use crate::tests::check_edit;
75
76     #[test]
77     fn literal_struct_completion_edit() {
78         check_edit(
79             "FooDesc {…}",
80             r#"
81 struct FooDesc { pub bar: bool }
82
83 fn create_foo(foo_desc: &FooDesc) -> () { () }
84
85 fn baz() {
86     let foo = create_foo(&$0);
87 }
88             "#,
89             r#"
90 struct FooDesc { pub bar: bool }
91
92 fn create_foo(foo_desc: &FooDesc) -> () { () }
93
94 fn baz() {
95     let foo = create_foo(&FooDesc { bar: ${1:()} }$0);
96 }
97             "#,
98         )
99     }
100
101     #[test]
102     fn literal_struct_complexion_module() {
103         check_edit(
104             "FooDesc {…}",
105             r#"
106 mod _69latrick {
107     pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, pub bar: bool }
108     pub fn create_foo(foo_desc: &FooDesc) -> () { () }
109 }
110
111 fn baz() {
112     use _69latrick::*;
113
114     let foo = create_foo(&$0);
115 }
116             "#,
117             r#"
118 mod _69latrick {
119     pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, pub bar: bool }
120     pub fn create_foo(foo_desc: &FooDesc) -> () { () }
121 }
122
123 fn baz() {
124     use _69latrick::*;
125
126     let foo = create_foo(&FooDesc { six: ${1:()}, neuf: ${2:()}, bar: ${3:()} }$0);
127 }
128             "#,
129         );
130     }
131
132     #[test]
133     fn default_completion_edit() {
134         check_edit(
135             "..Default::default()",
136             r#"
137 //- minicore: default
138 struct Struct { foo: u32, bar: usize }
139
140 impl Default for Struct {
141     fn default() -> Self {}
142 }
143
144 fn foo() {
145     let other = Struct {
146         foo: 5,
147         .$0
148     };
149 }
150 "#,
151             r#"
152 struct Struct { foo: u32, bar: usize }
153
154 impl Default for Struct {
155     fn default() -> Self {}
156 }
157
158 fn foo() {
159     let other = Struct {
160         foo: 5,
161         ..Default::default()
162     };
163 }
164 "#,
165         );
166         check_edit(
167             "..Default::default()",
168             r#"
169 //- minicore: default
170 struct Struct { foo: u32, bar: usize }
171
172 impl Default for Struct {
173     fn default() -> Self {}
174 }
175
176 fn foo() {
177     let other = Struct {
178         foo: 5,
179         $0
180     };
181 }
182 "#,
183             r#"
184 struct Struct { foo: u32, bar: usize }
185
186 impl Default for Struct {
187     fn default() -> Self {}
188 }
189
190 fn foo() {
191     let other = Struct {
192         foo: 5,
193         ..Default::default()
194     };
195 }
196 "#,
197         );
198         check_edit(
199             "..Default::default()",
200             r#"
201 //- minicore: default
202 struct Struct { foo: u32, bar: usize }
203
204 impl Default for Struct {
205     fn default() -> Self {}
206 }
207
208 fn foo() {
209     let other = Struct {
210         foo: 5,
211         ..$0
212     };
213 }
214 "#,
215             r#"
216 struct Struct { foo: u32, bar: usize }
217
218 impl Default for Struct {
219     fn default() -> Self {}
220 }
221
222 fn foo() {
223     let other = Struct {
224         foo: 5,
225         ..Default::default()
226     };
227 }
228 "#,
229         );
230     }
231 }