Skip to content

Commit 794988c

Browse files
committed
feat: filter already present enum variants in match arms
1 parent 68fd1ce commit 794988c

File tree

8 files changed

+114
-16
lines changed

8 files changed

+114
-16
lines changed

crates/ide-completion/src/completions.rs

Lines changed: 16 additions & 9 deletions
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

@@ -538,18 +538,25 @@ fn enum_variants_with_paths(
538538
enum_: hir::Enum,
539539
impl_: &Option<ast::Impl>,
540540
cb: impl Fn(&mut Completions, &CompletionContext<'_>, hir::Variant, hir::ModPath),
541+
missing_variants: Option<Vec<Variant>>,
541542
) {
542-
let variants = enum_.variants(ctx.db);
543+
let mut process_variant = |variant: Variant| {
544+
let self_path = hir::ModPath::from_segments(
545+
hir::PathKind::Plain,
546+
iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))),
547+
);
548+
549+
cb(acc, ctx, variant, self_path);
550+
};
551+
552+
let variants = match missing_variants {
553+
Some(missing_variants) => missing_variants,
554+
None => enum_.variants(ctx.db),
555+
};
543556

544557
if let Some(impl_) = impl_.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) {
545558
if impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_)) {
546-
for &variant in &variants {
547-
let self_path = hir::ModPath::from_segments(
548-
hir::PathKind::Plain,
549-
iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))),
550-
);
551-
cb(acc, ctx, variant, self_path);
552-
}
559+
variants.iter().for_each(|variant| process_variant(*variant));
553560
}
554561
}
555562

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ pub(crate) fn complete_expr_path(
208208
|acc, ctx, variant, path| {
209209
acc.add_qualified_enum_variant(ctx, path_ctx, variant, path)
210210
},
211+
None,
211212
);
212213
}
213214
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ use ide_db::imports::{
55
insert_use::ImportScope,
66
};
77
use itertools::Itertools;
8-
use syntax::{
9-
ast::{self},
10-
AstNode, SyntaxNode, T,
11-
};
8+
use syntax::{ast, AstNode, SyntaxNode, T};
129

1310
use crate::{
1411
context::{

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pub(crate) fn complete_pattern(
5858
|acc, ctx, variant, path| {
5959
acc.add_qualified_variant_pat(ctx, pattern_ctx, variant, path);
6060
},
61+
Some(pattern_ctx.missing_variants.clone()),
6162
);
6263
}
6364
}

crates/ide-completion/src/context.rs

Lines changed: 2 additions & 0 deletions
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

Lines changed: 51 additions & 2 deletions
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},
@@ -1111,6 +1111,9 @@ fn pattern_context_for(
11111111
pat: ast::Pat,
11121112
) -> PatternContext {
11131113
let mut param_ctx = None;
1114+
1115+
let mut missing_variants = vec![];
1116+
11141117
let (refutability, has_type_ascription) =
11151118
pat
11161119
.syntax()
@@ -1140,7 +1143,52 @@ fn pattern_context_for(
11401143
})();
11411144
return (PatternRefutability::Irrefutable, has_type_ascription)
11421145
},
1143-
ast::MatchArm(_) => PatternRefutability::Refutable,
1146+
ast::MatchArm(match_arm) => {
1147+
let missing_variants_opt = match_arm
1148+
.syntax()
1149+
.parent()
1150+
.and_then(ast::MatchArmList::cast)
1151+
.and_then(|match_arm_list| {
1152+
match_arm_list
1153+
.syntax()
1154+
.parent()
1155+
.and_then(ast::MatchExpr::cast)
1156+
.and_then(|match_expr| {
1157+
let expr_opt = find_opt_node_in_file(&original_file, match_expr.expr());
1158+
1159+
expr_opt.and_then(|expr| {
1160+
sema.type_of_expr(&expr)?
1161+
.adjusted()
1162+
.autoderef(sema.db)
1163+
.find_map(|ty| match ty.as_adt() {
1164+
Some(hir::Adt::Enum(e)) => Some(e),
1165+
_ => None,
1166+
}).and_then(|enum_| {
1167+
Some(enum_.variants(sema.db))
1168+
})
1169+
})
1170+
}).and_then(|variants| {
1171+
Some(variants.iter().filter_map(|variant| {
1172+
let variant_name = variant.name(sema.db).to_string();
1173+
1174+
let variant_already_present = match_arm_list.arms().any(|arm| {
1175+
arm.pat().and_then(|pat| {
1176+
let pat_already_present = pat.syntax().to_string().contains(&variant_name);
1177+
pat_already_present.then(|| pat_already_present)
1178+
}).is_some()
1179+
});
1180+
1181+
(!variant_already_present).then_some(variant.clone())
1182+
}).collect::<Vec<Variant>>())
1183+
})
1184+
});
1185+
1186+
if let Some(missing_variants_) = missing_variants_opt {
1187+
missing_variants = missing_variants_;
1188+
};
1189+
1190+
PatternRefutability::Refutable
1191+
},
11441192
ast::LetExpr(_) => PatternRefutability::Refutable,
11451193
ast::ForExpr(_) => PatternRefutability::Irrefutable,
11461194
_ => PatternRefutability::Irrefutable,
@@ -1162,6 +1210,7 @@ fn pattern_context_for(
11621210
ref_token,
11631211
record_pat: None,
11641212
impl_: fetch_immediate_impl(sema, original_file, pat.syntax()),
1213+
missing_variants,
11651214
}
11661215
}
11671216

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub(crate) fn render_struct_pat(
3838
let lookup = format_literal_lookup(name.as_str(), kind);
3939
let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?;
4040

41-
Some(build_completion(ctx, label, lookup, pat, strukt, false))
41+
Some(build_completion(ctx, label, lookup, pat, strukt, true))
4242
}
4343

4444
pub(crate) fn render_variant_pat(

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,47 @@ fn foo(s: Struct) {
4646
);
4747
}
4848

49+
#[test]
50+
fn record_pattern_field_enum() {
51+
check(
52+
r#"
53+
enum Baz { FOO, BAR }
54+
55+
fn foo(baz: Baz) {
56+
match baz {
57+
Baz::FOO => (),
58+
$0
59+
}
60+
}
61+
"#,
62+
expect![[r#"
63+
en Baz
64+
bn Baz::BAR Baz::BAR$0
65+
kw mut
66+
kw ref
67+
"#]],
68+
);
69+
70+
check(
71+
r#"
72+
enum Baz { FOO, BAR }
73+
74+
fn foo(baz: Baz) {
75+
match baz {
76+
FOO => (),
77+
$0
78+
}
79+
}
80+
"#,
81+
expect![[r#"
82+
en Baz
83+
bn Baz::BAR Baz::BAR$0
84+
kw mut
85+
kw ref
86+
"#]],
87+
);
88+
}
89+
4990
#[test]
5091
fn pattern_enum_variant() {
5192
check(

0 commit comments

Comments
 (0)