1 //! Completes constants and paths in unqualified patterns.
2
3 use hir::{db::DefDatabase, AssocItem, ScopeDef};
4 use syntax::ast::Pat;
5
6 use crate::{
7 context::{PathCompletionCtx, PatternContext, PatternRefutability, Qualified},
8 CompletionContext, Completions,
9 };
10
11 /// Completes constants and paths in unqualified patterns.
complete_pattern( acc: &mut Completions, ctx: &CompletionContext<'_>, pattern_ctx: &PatternContext, )12 pub(crate) fn complete_pattern(
13 acc: &mut Completions,
14 ctx: &CompletionContext<'_>,
15 pattern_ctx: &PatternContext,
16 ) {
17 match pattern_ctx.parent_pat.as_ref() {
18 Some(Pat::RangePat(_) | Pat::BoxPat(_)) => (),
19 Some(Pat::RefPat(r)) => {
20 if r.mut_token().is_none() {
21 acc.add_keyword(ctx, "mut");
22 }
23 }
24 _ => {
25 let tok = ctx.token.text_range().start();
26 match (pattern_ctx.ref_token.as_ref(), pattern_ctx.mut_token.as_ref()) {
27 (None, None) => {
28 acc.add_keyword(ctx, "ref");
29 acc.add_keyword(ctx, "mut");
30 }
31 (None, Some(m)) if tok < m.text_range().start() => {
32 acc.add_keyword(ctx, "ref");
33 }
34 (Some(r), None) if tok > r.text_range().end() => {
35 acc.add_keyword(ctx, "mut");
36 }
37 _ => (),
38 }
39 }
40 }
41
42 if pattern_ctx.record_pat.is_some() {
43 return;
44 }
45
46 let refutable = pattern_ctx.refutability == PatternRefutability::Refutable;
47 let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1;
48
49 if let Some(hir::Adt::Enum(e)) =
50 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
51 {
52 if refutable || single_variant_enum(e) {
53 super::enum_variants_with_paths(
54 acc,
55 ctx,
56 e,
57 &pattern_ctx.impl_,
58 |acc, ctx, variant, path| {
59 acc.add_qualified_variant_pat(ctx, pattern_ctx, variant, path);
60 },
61 );
62 }
63 }
64
65 // FIXME: ideally, we should look at the type we are matching against and
66 // suggest variants + auto-imports
67 ctx.process_all_names(&mut |name, res, _| {
68 let add_simple_path = match res {
69 hir::ScopeDef::ModuleDef(def) => match def {
70 hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
71 acc.add_struct_pat(ctx, pattern_ctx, strukt, Some(name.clone()));
72 true
73 }
74 hir::ModuleDef::Variant(variant)
75 if refutable || single_variant_enum(variant.parent_enum(ctx.db)) =>
76 {
77 acc.add_variant_pat(ctx, pattern_ctx, None, variant, Some(name.clone()));
78 true
79 }
80 hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
81 hir::ModuleDef::Const(..) => refutable,
82 hir::ModuleDef::Module(..) => true,
83 hir::ModuleDef::Macro(mac) => mac.is_fn_like(ctx.db),
84 _ => false,
85 },
86 hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() {
87 Some(hir::Adt::Struct(strukt)) => {
88 acc.add_struct_pat(ctx, pattern_ctx, strukt, Some(name.clone()));
89 true
90 }
91 Some(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
92 Some(hir::Adt::Union(_)) => true,
93 _ => false,
94 },
95 ScopeDef::GenericParam(hir::GenericParam::ConstParam(_)) => true,
96 ScopeDef::GenericParam(_)
97 | ScopeDef::AdtSelfType(_)
98 | ScopeDef::Local(_)
99 | ScopeDef::Label(_)
100 | ScopeDef::Unknown => false,
101 };
102 if add_simple_path {
103 acc.add_pattern_resolution(ctx, pattern_ctx, name, res);
104 }
105 });
106 }
107
108 pub(crate) fn complete_pattern_path(
109 acc: &mut Completions,
110 ctx: &CompletionContext<'_>,
111 path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
112 ) {
113 match qualified {
114 Qualified::With { resolution: Some(resolution), super_chain_len, .. } => {
115 acc.add_super_keyword(ctx, *super_chain_len);
116
117 match resolution {
118 hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
119 let module_scope = module.scope(ctx.db, Some(ctx.module));
120 for (name, def) in module_scope {
121 let add_resolution = match def {
122 ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => {
123 mac.is_fn_like(ctx.db)
124 }
125 ScopeDef::ModuleDef(_) => true,
126 _ => false,
127 };
128
129 if add_resolution {
130 acc.add_path_resolution(ctx, path_ctx, name, def, vec![]);
131 }
132 }
133 }
134 res => {
135 let ty = match res {
136 hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
137 hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
138 hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Struct(s))) => {
139 s.ty(ctx.db)
140 }
141 hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
142 e.ty(ctx.db)
143 }
144 hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Union(u))) => {
145 u.ty(ctx.db)
146 }
147 hir::PathResolution::Def(hir::ModuleDef::BuiltinType(ty)) => ty.ty(ctx.db),
148 hir::PathResolution::Def(hir::ModuleDef::TypeAlias(ty)) => ty.ty(ctx.db),
149 _ => return,
150 };
151
152 if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
153 acc.add_enum_variants(ctx, path_ctx, e);
154 }
155
156 ctx.iterate_path_candidates(&ty, |item| match item {
157 AssocItem::TypeAlias(ta) => acc.add_type_alias(ctx, ta),
158 AssocItem::Const(c) => acc.add_const(ctx, c),
159 _ => {}
160 });
161 }
162 }
163 }
164 Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
165 Qualified::No => {
166 // this will only be hit if there are brackets or braces, otherwise this will be parsed as an ident pattern
167 ctx.process_all_names(&mut |name, res, doc_aliases| {
168 // FIXME: we should check what kind of pattern we are in and filter accordingly
169 let add_completion = match res {
170 ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
171 ScopeDef::ModuleDef(hir::ModuleDef::Adt(_)) => true,
172 ScopeDef::ModuleDef(hir::ModuleDef::Variant(_)) => true,
173 ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
174 ScopeDef::ImplSelfType(_) => true,
175 _ => false,
176 };
177 if add_completion {
178 acc.add_path_resolution(ctx, path_ctx, name, res, doc_aliases);
179 }
180 });
181
182 acc.add_nameref_keywords_with_colon(ctx);
183 }
184 Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
185 }
186 }
187