feat: filter already present enum variants in match arms

This commit is contained in:
feniljain 2022-12-17 16:58:42 +05:30
parent 68fd1ce313
commit 794988c53b
8 changed files with 114 additions and 16 deletions

View File

@ -23,7 +23,7 @@ pub(crate) mod env_vars;
use std::iter;
use hir::{known, ScopeDef};
use hir::{known, ScopeDef, Variant};
use ide_db::{imports::import_assets::LocatedImport, SymbolKind};
use syntax::ast;
@ -538,18 +538,25 @@ fn enum_variants_with_paths(
enum_: hir::Enum,
impl_: &Option<ast::Impl>,
cb: impl Fn(&mut Completions, &CompletionContext<'_>, hir::Variant, hir::ModPath),
missing_variants: Option<Vec<Variant>>,
) {
let variants = enum_.variants(ctx.db);
let mut process_variant = |variant: Variant| {
let self_path = hir::ModPath::from_segments(
hir::PathKind::Plain,
iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))),
);
cb(acc, ctx, variant, self_path);
};
let variants = match missing_variants {
Some(missing_variants) => missing_variants,
None => enum_.variants(ctx.db),
};
if let Some(impl_) = impl_.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) {
if impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_)) {
for &variant in &variants {
let self_path = hir::ModPath::from_segments(
hir::PathKind::Plain,
iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))),
);
cb(acc, ctx, variant, self_path);
}
variants.iter().for_each(|variant| process_variant(*variant));
}
}

View File

@ -208,6 +208,7 @@ pub(crate) fn complete_expr_path(
|acc, ctx, variant, path| {
acc.add_qualified_enum_variant(ctx, path_ctx, variant, path)
},
None,
);
}
}

View File

@ -5,10 +5,7 @@ use ide_db::imports::{
insert_use::ImportScope,
};
use itertools::Itertools;
use syntax::{
ast::{self},
AstNode, SyntaxNode, T,
};
use syntax::{ast, AstNode, SyntaxNode, T};
use crate::{
context::{

View File

@ -58,6 +58,7 @@ pub(crate) fn complete_pattern(
|acc, ctx, variant, path| {
acc.add_qualified_variant_pat(ctx, pattern_ctx, variant, path);
},
Some(pattern_ctx.missing_variants.clone()),
);
}
}

View File

@ -220,6 +220,8 @@ pub(super) struct PatternContext {
/// The record pattern this name or ref is a field of
pub(super) record_pat: Option<ast::RecordPat>,
pub(super) impl_: Option<ast::Impl>,
/// List of missing variants in a match expr
pub(super) missing_variants: Vec<hir::Variant>,
}
#[derive(Debug, Clone, PartialEq, Eq)]

View File

@ -1,7 +1,7 @@
//! Module responsible for analyzing the code surrounding the cursor for completion.
use std::iter;
use hir::{Semantics, Type, TypeInfo};
use hir::{Semantics, Type, TypeInfo, Variant};
use ide_db::{active_parameter::ActiveParameter, RootDatabase};
use syntax::{
algo::{find_node_at_offset, non_trivia_sibling},
@ -1111,6 +1111,9 @@ fn pattern_context_for(
pat: ast::Pat,
) -> PatternContext {
let mut param_ctx = None;
let mut missing_variants = vec![];
let (refutability, has_type_ascription) =
pat
.syntax()
@ -1140,7 +1143,52 @@ fn pattern_context_for(
})();
return (PatternRefutability::Irrefutable, has_type_ascription)
},
ast::MatchArm(_) => PatternRefutability::Refutable,
ast::MatchArm(match_arm) => {
let missing_variants_opt = match_arm
.syntax()
.parent()
.and_then(ast::MatchArmList::cast)
.and_then(|match_arm_list| {
match_arm_list
.syntax()
.parent()
.and_then(ast::MatchExpr::cast)
.and_then(|match_expr| {
let expr_opt = find_opt_node_in_file(&original_file, match_expr.expr());
expr_opt.and_then(|expr| {
sema.type_of_expr(&expr)?
.adjusted()
.autoderef(sema.db)
.find_map(|ty| match ty.as_adt() {
Some(hir::Adt::Enum(e)) => Some(e),
_ => None,
}).and_then(|enum_| {
Some(enum_.variants(sema.db))
})
})
}).and_then(|variants| {
Some(variants.iter().filter_map(|variant| {
let variant_name = variant.name(sema.db).to_string();
let variant_already_present = match_arm_list.arms().any(|arm| {
arm.pat().and_then(|pat| {
let pat_already_present = pat.syntax().to_string().contains(&variant_name);
pat_already_present.then(|| pat_already_present)
}).is_some()
});
(!variant_already_present).then_some(variant.clone())
}).collect::<Vec<Variant>>())
})
});
if let Some(missing_variants_) = missing_variants_opt {
missing_variants = missing_variants_;
};
PatternRefutability::Refutable
},
ast::LetExpr(_) => PatternRefutability::Refutable,
ast::ForExpr(_) => PatternRefutability::Irrefutable,
_ => PatternRefutability::Irrefutable,
@ -1162,6 +1210,7 @@ fn pattern_context_for(
ref_token,
record_pat: None,
impl_: fetch_immediate_impl(sema, original_file, pat.syntax()),
missing_variants,
}
}

View File

@ -38,7 +38,7 @@ pub(crate) fn render_struct_pat(
let lookup = format_literal_lookup(name.as_str(), kind);
let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?;
Some(build_completion(ctx, label, lookup, pat, strukt, false))
Some(build_completion(ctx, label, lookup, pat, strukt, true))
}
pub(crate) fn render_variant_pat(

View File

@ -46,6 +46,47 @@ fn foo(s: Struct) {
);
}
#[test]
fn record_pattern_field_enum() {
check(
r#"
enum Baz { FOO, BAR }
fn foo(baz: Baz) {
match baz {
Baz::FOO => (),
$0
}
}
"#,
expect![[r#"
en Baz
bn Baz::BAR Baz::BAR$0
kw mut
kw ref
"#]],
);
check(
r#"
enum Baz { FOO, BAR }
fn foo(baz: Baz) {
match baz {
FOO => (),
$0
}
}
"#,
expect![[r#"
en Baz
bn Baz::BAR Baz::BAR$0
kw mut
kw ref
"#]],
);
}
#[test]
fn pattern_enum_variant() {
check(