Skip to content

Commit 7c05f55

Browse files
committed
Auto merge of #13789 - feniljain:fix_enum_completion, r=Veykril
feat: show only missing variant suggestion for enums in patterns completion and bump them in list too Fixes #12438 ### Points to help in review: - This PR can be reviewed commit wise, first commit is about bumping enum variant completions up in the list of completions and second commit is about only showing enum variants which are not complete - I am calculating missing variants in analysis.rs by firstly locating the enum and then comparing each of it's variant's name and checking if arm string already contains that name, this is kinda hacky but I didn't want to implement complete missing_arms assist here as that would have been too bulky to run on each completion cycle ( if we can improve this somehow would appreciate some inputs on it ) ### Output: https://user-images.githubusercontent.com/49019259/208245540-57d7321b-b275-477e-bef0-b3a1ff8b7040.mov Relevant Zulip Discussion: https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer/topic/Issue.20.2312438
2 parents 924d277 + a79a76a commit 7c05f55

File tree

5 files changed

+147
-13
lines changed

5 files changed

+147
-13
lines changed

crates/ide-completion/src/completions.rs

+11-8
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub(crate) mod env_vars;
2323

2424
use std::iter;
2525

26-
use hir::{known, ScopeDef};
26+
use hir::{known, ScopeDef, Variant};
2727
use ide_db::{imports::import_assets::LocatedImport, SymbolKind};
2828
use syntax::ast;
2929

@@ -537,17 +537,20 @@ fn enum_variants_with_paths(
537537
impl_: &Option<ast::Impl>,
538538
cb: impl Fn(&mut Completions, &CompletionContext<'_>, hir::Variant, hir::ModPath),
539539
) {
540+
let mut process_variant = |variant: Variant| {
541+
let self_path = hir::ModPath::from_segments(
542+
hir::PathKind::Plain,
543+
iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))),
544+
);
545+
546+
cb(acc, ctx, variant, self_path);
547+
};
548+
540549
let variants = enum_.variants(ctx.db);
541550

542551
if let Some(impl_) = impl_.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) {
543552
if impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_)) {
544-
for &variant in &variants {
545-
let self_path = hir::ModPath::from_segments(
546-
hir::PathKind::Plain,
547-
iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))),
548-
);
549-
cb(acc, ctx, variant, self_path);
550-
}
553+
variants.iter().for_each(|variant| process_variant(*variant));
551554
}
552555
}
553556

crates/ide-completion/src/context.rs

+2
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ pub(super) struct PatternContext {
220220
/// The record pattern this name or ref is a field of
221221
pub(super) record_pat: Option<ast::RecordPat>,
222222
pub(super) impl_: Option<ast::Impl>,
223+
/// List of missing variants in a match expr
224+
pub(super) missing_variants: Vec<hir::Variant>,
223225
}
224226

225227
#[derive(Debug, Clone, PartialEq, Eq)]

crates/ide-completion/src/context/analysis.rs

+51-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Module responsible for analyzing the code surrounding the cursor for completion.
22
use std::iter;
33

4-
use hir::{Semantics, Type, TypeInfo};
4+
use hir::{Semantics, Type, TypeInfo, Variant};
55
use ide_db::{active_parameter::ActiveParameter, RootDatabase};
66
use syntax::{
77
algo::{find_node_at_offset, non_trivia_sibling},
@@ -1132,6 +1132,9 @@ fn pattern_context_for(
11321132
pat: ast::Pat,
11331133
) -> PatternContext {
11341134
let mut param_ctx = None;
1135+
1136+
let mut missing_variants = vec![];
1137+
11351138
let (refutability, has_type_ascription) =
11361139
pat
11371140
.syntax()
@@ -1161,7 +1164,52 @@ fn pattern_context_for(
11611164
})();
11621165
return (PatternRefutability::Irrefutable, has_type_ascription)
11631166
},
1164-
ast::MatchArm(_) => PatternRefutability::Refutable,
1167+
ast::MatchArm(match_arm) => {
1168+
let missing_variants_opt = match_arm
1169+
.syntax()
1170+
.parent()
1171+
.and_then(ast::MatchArmList::cast)
1172+
.and_then(|match_arm_list| {
1173+
match_arm_list
1174+
.syntax()
1175+
.parent()
1176+
.and_then(ast::MatchExpr::cast)
1177+
.and_then(|match_expr| {
1178+
let expr_opt = find_opt_node_in_file(&original_file, match_expr.expr());
1179+
1180+
expr_opt.and_then(|expr| {
1181+
sema.type_of_expr(&expr)?
1182+
.adjusted()
1183+
.autoderef(sema.db)
1184+
.find_map(|ty| match ty.as_adt() {
1185+
Some(hir::Adt::Enum(e)) => Some(e),
1186+
_ => None,
1187+
}).and_then(|enum_| {
1188+
Some(enum_.variants(sema.db))
1189+
})
1190+
})
1191+
}).and_then(|variants| {
1192+
Some(variants.iter().filter_map(|variant| {
1193+
let variant_name = variant.name(sema.db).to_string();
1194+
1195+
let variant_already_present = match_arm_list.arms().any(|arm| {
1196+
arm.pat().and_then(|pat| {
1197+
let pat_already_present = pat.syntax().to_string().contains(&variant_name);
1198+
pat_already_present.then(|| pat_already_present)
1199+
}).is_some()
1200+
});
1201+
1202+
(!variant_already_present).then_some(variant.clone())
1203+
}).collect::<Vec<Variant>>())
1204+
})
1205+
});
1206+
1207+
if let Some(missing_variants_) = missing_variants_opt {
1208+
missing_variants = missing_variants_;
1209+
};
1210+
1211+
PatternRefutability::Refutable
1212+
},
11651213
ast::LetExpr(_) => PatternRefutability::Refutable,
11661214
ast::ForExpr(_) => PatternRefutability::Irrefutable,
11671215
_ => PatternRefutability::Irrefutable,
@@ -1183,6 +1231,7 @@ fn pattern_context_for(
11831231
ref_token,
11841232
record_pat: None,
11851233
impl_: fetch_immediate_impl(sema, original_file, pat.syntax()),
1234+
missing_variants,
11861235
}
11871236
}
11881237

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

+23-3
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ pub(crate) fn render_struct_pat(
3737
let lookup = format_literal_lookup(name.as_str(), kind);
3838
let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?;
3939

40-
Some(build_completion(ctx, label, lookup, pat, strukt))
40+
let db = ctx.db();
41+
42+
Some(build_completion(ctx, label, lookup, pat, strukt, strukt.ty(db), false))
4143
}
4244

4345
pub(crate) fn render_variant_pat(
@@ -52,6 +54,7 @@ pub(crate) fn render_variant_pat(
5254

5355
let fields = variant.fields(ctx.db());
5456
let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, variant)?;
57+
let enum_ty = variant.parent_enum(ctx.db()).ty(ctx.db());
5558

5659
let (name, escaped_name) = match path {
5760
Some(path) => (path.unescaped().to_string().into(), path.to_string().into()),
@@ -81,7 +84,15 @@ pub(crate) fn render_variant_pat(
8184
}
8285
};
8386

84-
Some(build_completion(ctx, label, lookup, pat, variant))
87+
Some(build_completion(
88+
ctx,
89+
label,
90+
lookup,
91+
pat,
92+
variant,
93+
enum_ty,
94+
pattern_ctx.missing_variants.contains(&variant),
95+
))
8596
}
8697

8798
fn build_completion(
@@ -90,13 +101,22 @@ fn build_completion(
90101
lookup: SmolStr,
91102
pat: String,
92103
def: impl HasAttrs + Copy,
104+
adt_ty: hir::Type,
105+
// Missing in context of match statement completions
106+
is_variant_missing: bool,
93107
) -> CompletionItem {
108+
let mut relevance = ctx.completion_relevance();
109+
110+
if is_variant_missing {
111+
relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty);
112+
}
113+
94114
let mut item = CompletionItem::new(CompletionItemKind::Binding, ctx.source_range(), label);
95115
item.set_documentation(ctx.docs(def))
96116
.set_deprecated(ctx.is_deprecated(def))
97117
.detail(&pat)
98118
.lookup_by(lookup)
99-
.set_relevance(ctx.completion_relevance());
119+
.set_relevance(relevance);
100120
match ctx.snippet_cap() {
101121
Some(snippet_cap) => item.insert_snippet(snippet_cap, pat),
102122
None => item.insert_text(pat),

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

+60
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,66 @@ fn foo(s: Struct) {
4646
);
4747
}
4848

49+
#[test]
50+
fn record_pattern_field_enum() {
51+
check(
52+
r#"
53+
//- minicore:result
54+
enum Baz { Foo, Bar }
55+
56+
fn foo(baz: Baz) {
57+
match baz {
58+
Baz::Foo => (),
59+
$0
60+
}
61+
}
62+
"#,
63+
expect![[r#"
64+
en Baz
65+
en Result
66+
md core
67+
ev Err
68+
ev Ok
69+
bn Baz::Bar Baz::Bar$0
70+
bn Baz::Foo Baz::Foo$0
71+
bn Err(…) Err($1)$0
72+
bn Ok(…) Ok($1)$0
73+
kw mut
74+
kw ref
75+
"#]],
76+
);
77+
78+
check(
79+
r#"
80+
//- minicore:result
81+
enum Baz { Foo, Bar }
82+
83+
fn foo(baz: Baz) {
84+
use Baz::*;
85+
match baz {
86+
Foo => (),
87+
$0
88+
}
89+
}
90+
"#,
91+
expect![[r#"
92+
en Baz
93+
en Result
94+
md core
95+
ev Bar
96+
ev Err
97+
ev Foo
98+
ev Ok
99+
bn Bar Bar$0
100+
bn Err(…) Err($1)$0
101+
bn Foo Foo$0
102+
bn Ok(…) Ok($1)$0
103+
kw mut
104+
kw ref
105+
"#]],
106+
);
107+
}
108+
49109
#[test]
50110
fn pattern_enum_variant() {
51111
check(

0 commit comments

Comments
 (0)