Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/completions/pattern.rs')
-rw-r--r--crates/ide-completion/src/completions/pattern.rs208
1 files changed, 208 insertions, 0 deletions
diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs
new file mode 100644
index 0000000000..16666eba45
--- /dev/null
+++ b/crates/ide-completion/src/completions/pattern.rs
@@ -0,0 +1,208 @@
+//! Completes constants and paths in unqualified patterns.
+
+use hir::{db::DefDatabase, AssocItem, ScopeDef};
+use ide_db::FxHashSet;
+use syntax::ast::Pat;
+
+use crate::{
+ context::{PathCompletionCtx, PathQualifierCtx, PatternRefutability},
+ CompletionContext, Completions,
+};
+
+/// Completes constants and paths in unqualified patterns.
+pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
+ let patctx = match &ctx.pattern_ctx {
+ Some(ctx) => ctx,
+ _ => return,
+ };
+ let refutable = patctx.refutability == PatternRefutability::Refutable;
+
+ if let Some(path_ctx) = &ctx.path_context {
+ pattern_path_completion(acc, ctx, path_ctx);
+ return;
+ }
+
+ match patctx.parent_pat.as_ref() {
+ Some(Pat::RangePat(_) | Pat::BoxPat(_)) => (),
+ Some(Pat::RefPat(r)) => {
+ if r.mut_token().is_none() {
+ acc.add_keyword(ctx, "mut");
+ }
+ }
+ _ => {
+ let tok = ctx.token.text_range().start();
+ match (patctx.ref_token.as_ref(), patctx.mut_token.as_ref()) {
+ (None, None) => {
+ acc.add_keyword(ctx, "ref");
+ acc.add_keyword(ctx, "mut");
+ }
+ (None, Some(m)) if tok < m.text_range().start() => {
+ acc.add_keyword(ctx, "ref");
+ }
+ (Some(r), None) if tok > r.text_range().end() => {
+ acc.add_keyword(ctx, "mut");
+ }
+ _ => (),
+ }
+ }
+ }
+
+ let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1;
+
+ if let Some(hir::Adt::Enum(e)) =
+ ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
+ {
+ if refutable || single_variant_enum(e) {
+ super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
+ acc.add_qualified_variant_pat(ctx, variant, path);
+ });
+ }
+ }
+
+ // FIXME: ideally, we should look at the type we are matching against and
+ // suggest variants + auto-imports
+ ctx.process_all_names(&mut |name, res| {
+ let add_simple_path = match res {
+ hir::ScopeDef::ModuleDef(def) => match def {
+ hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
+ acc.add_struct_pat(ctx, strukt, Some(name.clone()));
+ true
+ }
+ hir::ModuleDef::Variant(variant)
+ if refutable || single_variant_enum(variant.parent_enum(ctx.db)) =>
+ {
+ acc.add_variant_pat(ctx, variant, Some(name.clone()));
+ true
+ }
+ hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
+ hir::ModuleDef::Const(..) => refutable,
+ hir::ModuleDef::Module(..) => true,
+ hir::ModuleDef::Macro(mac) if mac.is_fn_like(ctx.db) => {
+ return acc.add_macro(ctx, mac, name)
+ }
+ _ => false,
+ },
+ hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() {
+ Some(hir::Adt::Struct(strukt)) => {
+ acc.add_struct_pat(ctx, strukt, Some(name.clone()));
+ true
+ }
+ Some(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
+ Some(hir::Adt::Union(_)) => true,
+ _ => false,
+ },
+ ScopeDef::GenericParam(hir::GenericParam::ConstParam(_)) => true,
+ ScopeDef::GenericParam(_)
+ | ScopeDef::AdtSelfType(_)
+ | ScopeDef::Local(_)
+ | ScopeDef::Label(_)
+ | ScopeDef::Unknown => false,
+ };
+ if add_simple_path {
+ acc.add_resolution_simple(ctx, name, res);
+ }
+ });
+}
+
+fn pattern_path_completion(
+ acc: &mut Completions,
+ ctx: &CompletionContext,
+ PathCompletionCtx { qualifier, is_absolute_path, .. }: &PathCompletionCtx,
+) {
+ match qualifier {
+ Some(PathQualifierCtx { resolution, is_super_chain, .. }) => {
+ if *is_super_chain {
+ acc.add_keyword(ctx, "super::");
+ }
+
+ let resolution = match resolution {
+ Some(it) => it,
+ None => return,
+ };
+
+ match resolution {
+ hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
+ let module_scope = module.scope(ctx.db, Some(ctx.module));
+ for (name, def) in module_scope {
+ let add_resolution = match def {
+ ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => {
+ mac.is_fn_like(ctx.db)
+ }
+ ScopeDef::ModuleDef(_) => true,
+ _ => false,
+ };
+
+ if add_resolution {
+ acc.add_resolution(ctx, name, def);
+ }
+ }
+ }
+ res @ (hir::PathResolution::TypeParam(_)
+ | hir::PathResolution::SelfType(_)
+ | hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Struct(_)))
+ | hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(_)))
+ | hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Union(_)))
+ | hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_))) => {
+ let ty = match res {
+ hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
+ hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
+ hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Struct(s))) => {
+ s.ty(ctx.db)
+ }
+ hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
+ cov_mark::hit!(enum_plain_qualified_use_tree);
+ e.variants(ctx.db)
+ .into_iter()
+ .for_each(|variant| acc.add_enum_variant(ctx, variant, None));
+ e.ty(ctx.db)
+ }
+ hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Union(u))) => {
+ u.ty(ctx.db)
+ }
+ hir::PathResolution::Def(hir::ModuleDef::BuiltinType(ty)) => ty.ty(ctx.db),
+ _ => return,
+ };
+
+ let traits_in_scope = ctx.scope.visible_traits();
+ let mut seen = FxHashSet::default();
+ ty.iterate_path_candidates(
+ ctx.db,
+ &ctx.scope,
+ &traits_in_scope,
+ Some(ctx.module),
+ None,
+ |item| {
+ match item {
+ AssocItem::TypeAlias(ta) => {
+ // We might iterate candidates of a trait multiple times here, so deduplicate them.
+ if seen.insert(item) {
+ acc.add_type_alias(ctx, ta);
+ }
+ }
+ AssocItem::Const(c) => {
+ if seen.insert(item) {
+ acc.add_const(ctx, c);
+ }
+ }
+ _ => {}
+ }
+ None::<()>
+ },
+ );
+ }
+ _ => {}
+ }
+ }
+ // qualifier can only be none here if we are in a TuplePat or RecordPat in which case special characters have to follow the path
+ None if *is_absolute_path => acc.add_crate_roots(ctx),
+ None => {
+ cov_mark::hit!(unqualified_path_only_modules_in_import);
+ ctx.process_all_names(&mut |name, res| {
+ if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
+ acc.add_resolution(ctx, name, res);
+ }
+ });
+ acc.add_nameref_keywords_with_colon(ctx);
+ }
+ }
+}