ide_completion/completions/
pattern.rs

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