Skip to content

Commit d186986

Browse files
committed
Auto merge of #12986 - Veykril:completions, r=Veykril
Fix pattern field completions not working for unions
2 parents e1e93c4 + b3ac58d commit d186986

File tree

5 files changed

+129
-98
lines changed

5 files changed

+129
-98
lines changed

crates/ide-completion/src/completions.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,6 @@ pub(super) fn complete_name_ref(
617617

618618
dot::complete_undotted_self(acc, ctx, path_ctx, expr_ctx);
619619
item_list::complete_item_list_in_expr(acc, ctx, path_ctx, expr_ctx);
620-
record::complete_record_expr_func_update(acc, ctx, path_ctx, expr_ctx);
621620
snippet::complete_expr_snippet(acc, ctx, path_ctx, expr_ctx);
622621
}
623622
PathKind::Type { location } => {

crates/ide-completion/src/completions/expr.rs

Lines changed: 66 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
//! Completion of names from the current scope in expression position.
22
33
use hir::ScopeDef;
4+
use syntax::ast;
45

56
use crate::{
7+
completions::record::add_default_update,
68
context::{ExprCtx, PathCompletionCtx, Qualified},
79
CompletionContext, Completions,
810
};
@@ -219,60 +221,78 @@ pub(crate) fn complete_expr_path(
219221
_ => (),
220222
});
221223

222-
if is_func_update.is_none() {
223-
let mut add_keyword =
224-
|kw, snippet| acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet);
224+
match is_func_update {
225+
Some(record_expr) => {
226+
let ty = ctx.sema.type_of_expr(&ast::Expr::RecordExpr(record_expr.clone()));
225227

226-
if !in_block_expr {
227-
add_keyword("unsafe", "unsafe {\n $0\n}");
228-
}
229-
add_keyword("match", "match $1 {\n $0\n}");
230-
add_keyword("while", "while $1 {\n $0\n}");
231-
add_keyword("while let", "while let $1 = $2 {\n $0\n}");
232-
add_keyword("loop", "loop {\n $0\n}");
233-
if in_match_guard {
234-
add_keyword("if", "if $0");
235-
} else {
236-
add_keyword("if", "if $1 {\n $0\n}");
228+
match ty.as_ref().and_then(|t| t.original.as_adt()) {
229+
Some(hir::Adt::Union(_)) => (),
230+
_ => {
231+
cov_mark::hit!(functional_update);
232+
let missing_fields =
233+
ctx.sema.record_literal_missing_fields(record_expr);
234+
if !missing_fields.is_empty() {
235+
add_default_update(acc, ctx, ty);
236+
}
237+
}
238+
};
237239
}
238-
add_keyword("if let", "if let $1 = $2 {\n $0\n}");
239-
add_keyword("for", "for $1 in $2 {\n $0\n}");
240-
add_keyword("true", "true");
241-
add_keyword("false", "false");
240+
None => {
241+
let mut add_keyword = |kw, snippet| {
242+
acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet)
243+
};
242244

243-
if in_condition || in_block_expr {
244-
add_keyword("let", "let");
245-
}
245+
if !in_block_expr {
246+
add_keyword("unsafe", "unsafe {\n $0\n}");
247+
}
248+
add_keyword("match", "match $1 {\n $0\n}");
249+
add_keyword("while", "while $1 {\n $0\n}");
250+
add_keyword("while let", "while let $1 = $2 {\n $0\n}");
251+
add_keyword("loop", "loop {\n $0\n}");
252+
if in_match_guard {
253+
add_keyword("if", "if $0");
254+
} else {
255+
add_keyword("if", "if $1 {\n $0\n}");
256+
}
257+
add_keyword("if let", "if let $1 = $2 {\n $0\n}");
258+
add_keyword("for", "for $1 in $2 {\n $0\n}");
259+
add_keyword("true", "true");
260+
add_keyword("false", "false");
246261

247-
if after_if_expr {
248-
add_keyword("else", "else {\n $0\n}");
249-
add_keyword("else if", "else if $1 {\n $0\n}");
250-
}
262+
if in_condition || in_block_expr {
263+
add_keyword("let", "let");
264+
}
251265

252-
if wants_mut_token {
253-
add_keyword("mut", "mut ");
254-
}
266+
if after_if_expr {
267+
add_keyword("else", "else {\n $0\n}");
268+
add_keyword("else if", "else if $1 {\n $0\n}");
269+
}
255270

256-
if in_loop_body {
257-
if in_block_expr {
258-
add_keyword("continue", "continue;");
259-
add_keyword("break", "break;");
260-
} else {
261-
add_keyword("continue", "continue");
262-
add_keyword("break", "break");
271+
if wants_mut_token {
272+
add_keyword("mut", "mut ");
273+
}
274+
275+
if in_loop_body {
276+
if in_block_expr {
277+
add_keyword("continue", "continue;");
278+
add_keyword("break", "break;");
279+
} else {
280+
add_keyword("continue", "continue");
281+
add_keyword("break", "break");
282+
}
263283
}
264-
}
265284

266-
if let Some(ty) = innermost_ret_ty {
267-
add_keyword(
268-
"return",
269-
match (in_block_expr, ty.is_unit()) {
270-
(true, true) => "return ;",
271-
(true, false) => "return;",
272-
(false, true) => "return $0",
273-
(false, false) => "return",
274-
},
275-
);
285+
if let Some(ty) = innermost_ret_ty {
286+
add_keyword(
287+
"return",
288+
match (in_block_expr, ty.is_unit()) {
289+
(true, true) => "return ;",
290+
(true, false) => "return;",
291+
(false, true) => "return $0",
292+
(false, false) => "return",
293+
},
294+
);
295+
}
276296
}
277297
}
278298
}

crates/ide-completion/src/completions/record.rs

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use ide_db::SymbolKind;
33
use syntax::ast::{self, Expr};
44

55
use crate::{
6-
context::{DotAccess, DotAccessKind, ExprCtx, PathCompletionCtx, PatternContext, Qualified},
6+
context::{DotAccess, DotAccessKind, PatternContext},
77
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
88
CompletionRelevancePostfixMatch, Completions,
99
};
@@ -14,7 +14,24 @@ pub(crate) fn complete_record_pattern_fields(
1414
pattern_ctx: &PatternContext,
1515
) {
1616
if let PatternContext { record_pat: Some(record_pat), .. } = pattern_ctx {
17-
complete_fields(acc, ctx, ctx.sema.record_pattern_missing_fields(record_pat));
17+
let ty = ctx.sema.type_of_pat(&ast::Pat::RecordPat(record_pat.clone()));
18+
let missing_fields = match ty.as_ref().and_then(|t| t.original.as_adt()) {
19+
Some(hir::Adt::Union(un)) => {
20+
// ctx.sema.record_pat_missing_fields will always return
21+
// an empty Vec on a union literal. This is normally
22+
// reasonable, but here we'd like to present the full list
23+
// of fields if the literal is empty.
24+
let were_fields_specified =
25+
record_pat.record_pat_field_list().and_then(|fl| fl.fields().next()).is_some();
26+
27+
match were_fields_specified {
28+
false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
29+
true => return,
30+
}
31+
}
32+
_ => ctx.sema.record_pattern_missing_fields(record_pat),
33+
};
34+
complete_fields(acc, ctx, missing_fields);
1835
}
1936
}
2037

@@ -42,8 +59,13 @@ pub(crate) fn complete_record_expr_fields(
4259
}
4360
_ => {
4461
let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
45-
add_default_update(acc, ctx, ty, &missing_fields);
62+
63+
if !missing_fields.is_empty() {
64+
cov_mark::hit!(functional_update_field);
65+
add_default_update(acc, ctx, ty);
66+
}
4667
if dot_prefix {
68+
cov_mark::hit!(functional_update_one_dot);
4769
let mut item =
4870
CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
4971
item.insert_text(".");
@@ -56,41 +78,18 @@ pub(crate) fn complete_record_expr_fields(
5678
complete_fields(acc, ctx, missing_fields);
5779
}
5880

59-
// FIXME: This should probably be part of complete_path_expr
60-
pub(crate) fn complete_record_expr_func_update(
61-
acc: &mut Completions,
62-
ctx: &CompletionContext<'_>,
63-
path_ctx: &PathCompletionCtx,
64-
expr_ctx: &ExprCtx,
65-
) {
66-
if !matches!(path_ctx.qualified, Qualified::No) {
67-
return;
68-
}
69-
if let ExprCtx { is_func_update: Some(record_expr), .. } = expr_ctx {
70-
let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
71-
72-
match ty.as_ref().and_then(|t| t.original.as_adt()) {
73-
Some(hir::Adt::Union(_)) => (),
74-
_ => {
75-
let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
76-
add_default_update(acc, ctx, ty, &missing_fields);
77-
}
78-
};
79-
}
80-
}
81-
82-
fn add_default_update(
81+
pub(crate) fn add_default_update(
8382
acc: &mut Completions,
8483
ctx: &CompletionContext<'_>,
8584
ty: Option<hir::TypeInfo>,
86-
missing_fields: &[(hir::Field, hir::Type)],
8785
) {
8886
let default_trait = ctx.famous_defs().core_default_Default();
89-
let impl_default_trait = default_trait
87+
let impls_default_trait = default_trait
9088
.zip(ty.as_ref())
9189
.map_or(false, |(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[]));
92-
if impl_default_trait && !missing_fields.is_empty() {
90+
if impls_default_trait {
9391
// FIXME: This should make use of scope_def like completions so we get all the other goodies
92+
// that is we should handle this like actually completing the default function
9493
let completion_text = "..Default::default()";
9594
let mut item = CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
9695
let completion_text =

crates/ide-completion/src/context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ pub(crate) struct ExprCtx {
134134
pub(crate) in_condition: bool,
135135
pub(crate) incomplete_let: bool,
136136
pub(crate) ref_expr_parent: Option<ast::RefExpr>,
137+
/// The surrounding RecordExpression we are completing a functional update
137138
pub(crate) is_func_update: Option<ast::RecordExpr>,
138139
pub(crate) self_param: Option<hir::SelfParam>,
139140
pub(crate) innermost_ret_ty: Option<hir::Type>,

crates/ide-completion/src/tests/record.rs

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,9 @@ fn foo(f: Struct) {
103103
}
104104

105105
#[test]
106-
fn functional_update() {
107-
// FIXME: This should filter out all completions that do not have the type `Foo`
106+
fn in_functional_update() {
107+
cov_mark::check!(functional_update);
108+
108109
check(
109110
r#"
110111
//- minicore:default
@@ -116,13 +117,21 @@ impl Default for Foo {
116117
fn main() {
117118
let thing = 1;
118119
let foo = Foo { foo1: 0, foo2: 0 };
119-
let foo2 = Foo { thing, $0 }
120+
let foo2 = Foo { thing, ..$0 }
120121
}
121122
"#,
122123
expect![[r#"
123124
fd ..Default::default()
124-
fd foo1 u32
125-
fd foo2 u32
125+
fn main() fn()
126+
lc foo Foo
127+
lc thing i32
128+
md core
129+
st Foo
130+
st Foo {…} Foo { foo1: u32, foo2: u32 }
131+
tt Default
132+
bt u32
133+
kw crate::
134+
kw self::
126135
"#]],
127136
);
128137
check(
@@ -136,14 +145,19 @@ impl Default for Foo {
136145
fn main() {
137146
let thing = 1;
138147
let foo = Foo { foo1: 0, foo2: 0 };
139-
let foo2 = Foo { thing, .$0 }
148+
let foo2 = Foo { thing, ..Default::$0 }
140149
}
141150
"#,
142151
expect![[r#"
143-
fd ..Default::default()
144-
sn ..
152+
fn default() (as Default) fn() -> Self
145153
"#]],
146154
);
155+
}
156+
157+
#[test]
158+
fn functional_update_no_dot() {
159+
cov_mark::check!(functional_update_field);
160+
// FIXME: This should filter out all completions that do not have the type `Foo`
147161
check(
148162
r#"
149163
//- minicore:default
@@ -155,23 +169,20 @@ impl Default for Foo {
155169
fn main() {
156170
let thing = 1;
157171
let foo = Foo { foo1: 0, foo2: 0 };
158-
let foo2 = Foo { thing, ..$0 }
172+
let foo2 = Foo { thing, $0 }
159173
}
160174
"#,
161175
expect![[r#"
162176
fd ..Default::default()
163-
fn main() fn()
164-
lc foo Foo
165-
lc thing i32
166-
md core
167-
st Foo
168-
st Foo {…} Foo { foo1: u32, foo2: u32 }
169-
tt Default
170-
bt u32
171-
kw crate::
172-
kw self::
177+
fd foo1 u32
178+
fd foo2 u32
173179
"#]],
174180
);
181+
}
182+
183+
#[test]
184+
fn functional_update_one_dot() {
185+
cov_mark::check!(functional_update_one_dot);
175186
check(
176187
r#"
177188
//- minicore:default
@@ -183,11 +194,12 @@ impl Default for Foo {
183194
fn main() {
184195
let thing = 1;
185196
let foo = Foo { foo1: 0, foo2: 0 };
186-
let foo2 = Foo { thing, ..Default::$0 }
197+
let foo2 = Foo { thing, .$0 }
187198
}
188199
"#,
189200
expect![[r#"
190-
fn default() (as Default) fn() -> Self
201+
fd ..Default::default()
202+
sn ..
191203
"#]],
192204
);
193205
}

0 commit comments

Comments
 (0)