Skip to content

Commit 8c9359b

Browse files
committed
Fix pattern field completions not working for unions
1 parent e1e93c4 commit 8c9359b

File tree

5 files changed

+118
-94
lines changed

5 files changed

+118
-94
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: 64 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,76 @@ 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+
add_default_update(acc, ctx, ty, &missing_fields);
235+
}
236+
};
237237
}
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");
238+
None => {
239+
let mut add_keyword = |kw, snippet| {
240+
acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet)
241+
};
242242

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

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-
}
260+
if in_condition || in_block_expr {
261+
add_keyword("let", "let");
262+
}
251263

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

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");
269+
if wants_mut_token {
270+
add_keyword("mut", "mut ");
271+
}
272+
273+
if in_loop_body {
274+
if in_block_expr {
275+
add_keyword("continue", "continue;");
276+
add_keyword("break", "break;");
277+
} else {
278+
add_keyword("continue", "continue");
279+
add_keyword("break", "break");
280+
}
263281
}
264-
}
265282

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-
);
283+
if let Some(ty) = innermost_ret_ty {
284+
add_keyword(
285+
"return",
286+
match (in_block_expr, ty.is_unit()) {
287+
(true, true) => "return ;",
288+
(true, false) => "return;",
289+
(false, true) => "return $0",
290+
(false, false) => "return",
291+
},
292+
);
293+
}
276294
}
277295
}
278296
}

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

Lines changed: 21 additions & 26 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

@@ -44,6 +61,7 @@ pub(crate) fn complete_record_expr_fields(
4461
let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
4562
add_default_update(acc, ctx, ty, &missing_fields);
4663
if dot_prefix {
64+
cov_mark::hit!(functional_update_one_dot);
4765
let mut item =
4866
CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
4967
item.insert_text(".");
@@ -56,30 +74,7 @@ pub(crate) fn complete_record_expr_fields(
5674
complete_fields(acc, ctx, missing_fields);
5775
}
5876

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(
77+
pub(crate) fn add_default_update(
8378
acc: &mut Completions,
8479
ctx: &CompletionContext<'_>,
8580
ty: Option<hir::TypeInfo>,

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: 32 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,18 @@ 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+
// FIXME: This should filter out all completions that do not have the type `Foo`
147160
check(
148161
r#"
149162
//- minicore:default
@@ -155,23 +168,20 @@ impl Default for Foo {
155168
fn main() {
156169
let thing = 1;
157170
let foo = Foo { foo1: 0, foo2: 0 };
158-
let foo2 = Foo { thing, ..$0 }
171+
let foo2 = Foo { thing, $0 }
159172
}
160173
"#,
161174
expect![[r#"
162175
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::
176+
fd foo1 u32
177+
fd foo2 u32
173178
"#]],
174179
);
180+
}
181+
182+
#[test]
183+
fn functional_update_one_dot() {
184+
cov_mark::check!(functional_update_one_dot);
175185
check(
176186
r#"
177187
//- minicore:default
@@ -183,11 +193,12 @@ impl Default for Foo {
183193
fn main() {
184194
let thing = 1;
185195
let foo = Foo { foo1: 0, foo2: 0 };
186-
let foo2 = Foo { thing, ..Default::$0 }
196+
let foo2 = Foo { thing, .$0 }
187197
}
188198
"#,
189199
expect![[r#"
190-
fn default() (as Default) fn() -> Self
200+
fd ..Default::default()
201+
sn ..
191202
"#]],
192203
);
193204
}

0 commit comments

Comments
 (0)