Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 4ca47b3

Browse files
committed
Auto merge of rust-lang#12373 - Veykril:completion, r=Veykril
internal: Refactor our record pat/expr handling in completion context
2 parents 7a4994d + 6a8b8a6 commit 4ca47b3

File tree

9 files changed

+153
-127
lines changed

9 files changed

+153
-127
lines changed

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

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use ide_db::FxHashSet;
55
use syntax::T;
66

77
use crate::{
8-
context::{PathCompletionCtx, PathKind, PathQualifierCtx},
8+
context::{NameRefContext, PathCompletionCtx, PathKind, PathQualifierCtx},
99
CompletionContext, Completions,
1010
};
1111

@@ -15,14 +15,25 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
1515
return;
1616
}
1717

18-
let (is_absolute_path, qualifier, in_block_expr, in_loop_body, in_functional_update) =
19-
match ctx.path_context() {
20-
Some(&PathCompletionCtx {
21-
kind: PathKind::Expr { in_block_expr, in_loop_body, in_functional_update },
22-
is_absolute_path,
23-
ref qualifier,
18+
let (is_absolute_path, qualifier, in_block_expr, in_loop_body, is_func_update) =
19+
match ctx.nameref_ctx() {
20+
Some(NameRefContext {
21+
path_ctx:
22+
Some(PathCompletionCtx {
23+
kind: PathKind::Expr { in_block_expr, in_loop_body },
24+
is_absolute_path,
25+
qualifier,
26+
..
27+
}),
28+
record_expr,
2429
..
25-
}) => (is_absolute_path, qualifier, in_block_expr, in_loop_body, in_functional_update),
30+
}) => (
31+
*is_absolute_path,
32+
qualifier,
33+
*in_block_expr,
34+
*in_loop_body,
35+
record_expr.as_ref().map_or(false, |&(_, it)| it),
36+
),
2637
_ => return,
2738
};
2839

@@ -165,7 +176,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
165176
}
166177
});
167178

168-
if !in_functional_update {
179+
if !is_func_update {
169180
let mut add_keyword =
170181
|kw, snippet| super::keyword::add_keyword(acc, ctx, kw, snippet);
171182

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
use syntax::T;
66

77
use crate::{
8-
context::PathKind, patterns::ImmediateLocation, CompletionContext, CompletionItem,
9-
CompletionItemKind, Completions,
8+
context::{NameRefContext, PathKind},
9+
CompletionContext, CompletionItem, CompletionItemKind, Completions,
1010
};
1111

1212
pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
13-
if matches!(ctx.completion_location, Some(ImmediateLocation::RecordExpr(_))) {
13+
if matches!(ctx.nameref_ctx(), Some(NameRefContext { record_expr: Some(_), .. })) {
1414
cov_mark::hit!(no_keyword_completion_in_record_lit);
1515
return;
1616
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
1515
Some(ctx) => ctx,
1616
_ => return,
1717
};
18-
let refutable = patctx.refutability == PatternRefutability::Refutable;
1918

2019
if let Some(path_ctx) = ctx.path_context() {
2120
pattern_path_completion(acc, ctx, path_ctx);
@@ -47,6 +46,11 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
4746
}
4847
}
4948

49+
if patctx.record_pat.is_some() {
50+
return;
51+
}
52+
53+
let refutable = patctx.refutability == PatternRefutability::Refutable;
5054
let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1;
5155

5256
if let Some(hir::Adt::Enum(e)) =

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

Lines changed: 54 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,67 +3,65 @@ use ide_db::SymbolKind;
33
use syntax::{ast::Expr, T};
44

55
use crate::{
6-
patterns::ImmediateLocation, CompletionContext, CompletionItem, CompletionItemKind,
7-
CompletionRelevance, CompletionRelevancePostfixMatch, Completions,
6+
context::{NameRefContext, PatternContext},
7+
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
8+
CompletionRelevancePostfixMatch, Completions,
89
};
910

1011
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-
18-
if let Some(hir::Adt::Union(un)) = ty.as_ref().and_then(|t| t.original.as_adt()) {
19-
// ctx.sema.record_literal_missing_fields will always return
20-
// an empty Vec on a union literal. This is normally
21-
// reasonable, but here we'd like to present the full list
22-
// of fields if the literal is empty.
23-
let were_fields_specified = record_expr
24-
.record_expr_field_list()
25-
.and_then(|fl| fl.fields().next())
26-
.is_some();
27-
28-
match were_fields_specified {
29-
false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
30-
true => vec![],
31-
}
32-
} else {
33-
let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
34-
35-
let default_trait = ctx.famous_defs().core_default_Default();
36-
let impl_default_trait =
37-
default_trait.zip(ty.as_ref()).map_or(false, |(default_trait, ty)| {
38-
ty.original.impls_trait(ctx.db, default_trait, &[])
39-
});
40-
41-
if impl_default_trait && !missing_fields.is_empty() && ctx.path_qual().is_none() {
42-
let completion_text = "..Default::default()";
43-
let mut item =
44-
CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
45-
let completion_text =
46-
completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text);
47-
item.insert_text(completion_text).set_relevance(CompletionRelevance {
48-
postfix_match: Some(CompletionRelevancePostfixMatch::Exact),
49-
..Default::default()
50-
});
51-
item.add_to(acc);
52-
}
53-
if ctx.previous_token_is(T![.]) {
54-
let mut item =
55-
CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
56-
item.insert_text(".");
57-
item.add_to(acc);
58-
return None;
59-
}
60-
missing_fields
12+
let missing_fields = if let Some(PatternContext { record_pat: Some(record_pat), .. }) =
13+
&ctx.pattern_ctx
14+
{
15+
ctx.sema.record_pattern_missing_fields(record_pat)
16+
} else if let Some(NameRefContext { record_expr: Some((record_expr, _)), .. }) =
17+
ctx.nameref_ctx()
18+
{
19+
let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
20+
21+
if let Some(hir::Adt::Union(un)) = ty.as_ref().and_then(|t| t.original.as_adt()) {
22+
// ctx.sema.record_literal_missing_fields will always return
23+
// an empty Vec on a union literal. This is normally
24+
// reasonable, but here we'd like to present the full list
25+
// of fields if the literal is empty.
26+
let were_fields_specified =
27+
record_expr.record_expr_field_list().and_then(|fl| fl.fields().next()).is_some();
28+
29+
match were_fields_specified {
30+
false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
31+
true => vec![],
6132
}
33+
} else {
34+
let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
35+
36+
let default_trait = ctx.famous_defs().core_default_Default();
37+
let impl_default_trait =
38+
default_trait.zip(ty.as_ref()).map_or(false, |(default_trait, ty)| {
39+
ty.original.impls_trait(ctx.db, default_trait, &[])
40+
});
41+
42+
if impl_default_trait && !missing_fields.is_empty() && ctx.path_qual().is_none() {
43+
let completion_text = "..Default::default()";
44+
let mut item =
45+
CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
46+
let completion_text =
47+
completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text);
48+
item.insert_text(completion_text).set_relevance(CompletionRelevance {
49+
postfix_match: Some(CompletionRelevancePostfixMatch::Exact),
50+
..Default::default()
51+
});
52+
item.add_to(acc);
53+
}
54+
if ctx.previous_token_is(T![.]) {
55+
let mut item =
56+
CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
57+
item.insert_text(".");
58+
item.add_to(acc);
59+
return None;
60+
}
61+
missing_fields
6262
}
63-
Some(ImmediateLocation::RecordPat(record_pat)) => {
64-
ctx.sema.record_pattern_missing_fields(record_pat)
65-
}
66-
_ => return None,
63+
} else {
64+
return None;
6765
};
6866

6967
for (field, ty) in missing_fields {

crates/ide-completion/src/context.rs

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ pub(super) enum PathKind {
4848
Expr {
4949
in_block_expr: bool,
5050
in_loop_body: bool,
51-
in_functional_update: bool,
5251
},
5352
Type,
5453
Attr {
@@ -115,6 +114,8 @@ pub(super) struct PatternContext {
115114
pub(super) parent_pat: Option<ast::Pat>,
116115
pub(super) ref_token: Option<SyntaxToken>,
117116
pub(super) mut_token: Option<SyntaxToken>,
117+
/// The record pattern this name or ref is a field of
118+
pub(super) record_pat: Option<ast::RecordPat>,
118119
}
119120

120121
#[derive(Debug)]
@@ -166,8 +167,11 @@ pub(super) enum NameKind {
166167
pub(super) struct NameRefContext {
167168
/// NameRef syntax in the original file
168169
pub(super) nameref: Option<ast::NameRef>,
170+
// FIXME: these fields are actually disjoint -> enum
169171
pub(super) dot_access: Option<DotAccess>,
170172
pub(super) path_ctx: Option<PathCompletionCtx>,
173+
/// The record expression this nameref is a field of
174+
pub(super) record_expr: Option<(ast::RecordExpr, bool)>,
171175
}
172176

173177
#[derive(Debug)]
@@ -375,14 +379,15 @@ impl<'a> CompletionContext<'a> {
375379
pub(crate) fn is_path_disallowed(&self) -> bool {
376380
self.previous_token_is(T![unsafe])
377381
|| matches!(self.prev_sibling, Some(ImmediatePrevSibling::Visibility))
378-
|| matches!(
379-
self.completion_location,
380-
Some(ImmediateLocation::RecordPat(_) | ImmediateLocation::RecordExpr(_))
381-
)
382382
|| matches!(
383383
self.name_ctx(),
384384
Some(NameContext { kind: NameKind::Module(_) | NameKind::Rename, .. })
385385
)
386+
|| matches!(self.pattern_ctx, Some(PatternContext { record_pat: Some(_), .. }))
387+
|| matches!(
388+
self.nameref_ctx(),
389+
Some(NameRefContext { record_expr: Some((_, false)), .. })
390+
)
386391
}
387392

388393
pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
@@ -1023,14 +1028,13 @@ impl<'a> CompletionContext<'a> {
10231028
ast::Enum(_) => NameKind::Enum,
10241029
ast::Fn(_) => NameKind::Function,
10251030
ast::IdentPat(bind_pat) => {
1026-
let is_name_in_field_pat = bind_pat
1027-
.syntax()
1028-
.parent()
1029-
.and_then(ast::RecordPatField::cast)
1030-
.map_or(false, |pat_field| pat_field.name_ref().is_none());
1031-
if !is_name_in_field_pat {
1032-
pat_ctx = Some(pattern_context_for(original_file, bind_pat.into()));
1033-
}
1031+
pat_ctx = Some({
1032+
let mut pat_ctx = pattern_context_for(original_file, bind_pat.into());
1033+
if let Some(record_field) = ast::RecordPatField::for_field_name(&name) {
1034+
pat_ctx.record_pat = find_node_in_file_compensated(original_file, &record_field.parent_record_pat());
1035+
}
1036+
pat_ctx
1037+
});
10341038

10351039
NameKind::IdentPat
10361040
},
@@ -1062,7 +1066,33 @@ impl<'a> CompletionContext<'a> {
10621066
) -> (NameRefContext, Option<PatternContext>) {
10631067
let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
10641068

1065-
let mut nameref_ctx = NameRefContext { dot_access: None, path_ctx: None, nameref };
1069+
let mut nameref_ctx =
1070+
NameRefContext { dot_access: None, path_ctx: None, nameref, record_expr: None };
1071+
1072+
if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
1073+
nameref_ctx.record_expr =
1074+
find_node_in_file_compensated(original_file, &record_field.parent_record_lit())
1075+
.zip(Some(false));
1076+
return (nameref_ctx, None);
1077+
}
1078+
if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) {
1079+
let pat_ctx =
1080+
pattern_context_for(original_file, record_field.parent_record_pat().clone().into());
1081+
return (
1082+
nameref_ctx,
1083+
Some(PatternContext {
1084+
param_ctx: None,
1085+
has_type_ascription: false,
1086+
ref_token: None,
1087+
mut_token: None,
1088+
record_pat: find_node_in_file_compensated(
1089+
original_file,
1090+
&record_field.parent_record_pat(),
1091+
),
1092+
..pat_ctx
1093+
}),
1094+
);
1095+
}
10661096

10671097
let segment = match_ast! {
10681098
match parent {
@@ -1115,8 +1145,11 @@ impl<'a> CompletionContext<'a> {
11151145
})
11161146
.unwrap_or(false)
11171147
};
1118-
let is_in_func_update = |it: &SyntaxNode| {
1119-
it.parent().map_or(false, |it| ast::RecordExprFieldList::can_cast(it.kind()))
1148+
let mut fill_record_expr = |syn: &SyntaxNode| {
1149+
if let Some(record_expr) = syn.ancestors().nth(2).and_then(ast::RecordExpr::cast) {
1150+
nameref_ctx.record_expr =
1151+
find_node_in_file_compensated(original_file, &record_expr).zip(Some(true));
1152+
}
11201153
};
11211154

11221155
let kind = path.syntax().ancestors().find_map(|it| {
@@ -1125,11 +1158,12 @@ impl<'a> CompletionContext<'a> {
11251158
match it {
11261159
ast::PathType(_) => Some(PathKind::Type),
11271160
ast::PathExpr(it) => {
1161+
fill_record_expr(it.syntax());
1162+
11281163
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
11291164
let in_block_expr = is_in_block(it.syntax());
11301165
let in_loop_body = is_in_loop_body(it.syntax());
1131-
let in_functional_update = is_in_func_update(it.syntax());
1132-
Some(PathKind::Expr { in_block_expr, in_loop_body, in_functional_update })
1166+
Some(PathKind::Expr { in_block_expr, in_loop_body })
11331167
},
11341168
ast::TupleStructPat(it) => {
11351169
path_ctx.has_call_parens = true;
@@ -1163,8 +1197,8 @@ impl<'a> CompletionContext<'a> {
11631197
return Some(parent.and_then(ast::MacroExpr::cast).map(|it| {
11641198
let in_loop_body = is_in_loop_body(it.syntax());
11651199
let in_block_expr = is_in_block(it.syntax());
1166-
let in_functional_update = is_in_func_update(it.syntax());
1167-
PathKind::Expr { in_block_expr, in_loop_body, in_functional_update }
1200+
fill_record_expr(it.syntax());
1201+
PathKind::Expr { in_block_expr, in_loop_body }
11681202
}));
11691203
},
11701204
}
@@ -1299,6 +1333,7 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont
12991333
parent_pat: pat.syntax().parent().and_then(ast::Pat::cast),
13001334
mut_token,
13011335
ref_token,
1336+
record_pat: None,
13021337
}
13031338
}
13041339

0 commit comments

Comments
 (0)