ide_completion/completions/
type.rs

1//! Completion of names from the current scope in type position.
2
3use hir::{HirDisplay, ScopeDef};
4use syntax::{AstNode, ast};
5
6use crate::{
7    CompletionContext, Completions,
8    context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation},
9    render::render_type_inference,
10};
11
12pub(crate) fn complete_type_path(
13    acc: &mut Completions,
14    ctx: &CompletionContext<'_>,
15    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
16    location: &TypeLocation,
17) {
18    let _p = tracing::info_span!("complete_type_path").entered();
19
20    let scope_def_applicable = |def| {
21        use hir::{GenericParam::*, ModuleDef::*};
22        match def {
23            ScopeDef::GenericParam(LifetimeParam(_)) => location.complete_lifetimes(),
24            ScopeDef::Label(_) => false,
25            // no values in type places
26            ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false,
27            // unless its a constant in a generic arg list position
28            ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => {
29                location.complete_consts()
30            }
31            ScopeDef::ImplSelfType(_) => location.complete_self_type(),
32            // Don't suggest attribute macros and derives.
33            ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),
34            ScopeDef::ModuleDef(Trait(_) | Module(_))
35                if matches!(location, TypeLocation::ImplTrait) =>
36            {
37                true
38            }
39            // Type things are fine
40            ScopeDef::ModuleDef(BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TypeAlias(_))
41            | ScopeDef::AdtSelfType(_)
42            | ScopeDef::Unknown
43            | ScopeDef::GenericParam(TypeParam(_)) => location.complete_types(),
44        }
45    };
46
47    let add_assoc_item = |acc: &mut Completions, item| match item {
48        hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArg { .. }) => {
49            acc.add_const(ctx, ct)
50        }
51        hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (),
52        hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
53    };
54
55    match qualified {
56        Qualified::TypeAnchor { ty: None, trait_: None } => ctx
57            .traits_in_scope()
58            .iter()
59            .flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db))
60            .for_each(|item| add_assoc_item(acc, item)),
61        Qualified::TypeAnchor { trait_: Some(trait_), .. } => {
62            trait_.items(ctx.sema.db).into_iter().for_each(|item| add_assoc_item(acc, item))
63        }
64        Qualified::TypeAnchor { ty: Some(ty), trait_: None } => {
65            ctx.iterate_path_candidates(ty, |item| {
66                add_assoc_item(acc, item);
67            });
68
69            // Iterate assoc types separately
70            ty.iterate_assoc_items(ctx.db, |item| {
71                if let hir::AssocItem::TypeAlias(ty) = item {
72                    acc.add_type_alias(ctx, ty)
73                }
74                None::<()>
75            });
76        }
77        Qualified::With { resolution: None, .. } => {}
78        Qualified::With { resolution: Some(resolution), .. } => {
79            // Add associated types on type parameters and `Self`.
80            ctx.scope.assoc_type_shorthand_candidates(resolution, |alias| {
81                acc.add_type_alias(ctx, alias);
82            });
83
84            match resolution {
85                hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
86                    let module_scope = module.scope(ctx.db, Some(ctx.module));
87                    for (name, def) in module_scope {
88                        if scope_def_applicable(def) {
89                            acc.add_path_resolution(ctx, path_ctx, name, def, vec![]);
90                        }
91                    }
92                }
93                hir::PathResolution::Def(
94                    def @ (hir::ModuleDef::Adt(_)
95                    | hir::ModuleDef::TypeAlias(_)
96                    | hir::ModuleDef::BuiltinType(_)),
97                ) => {
98                    let ty = match def {
99                        hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
100                        hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
101                        hir::ModuleDef::BuiltinType(builtin) => builtin.ty(ctx.db),
102                        _ => return,
103                    };
104
105                    // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
106                    // (where AssocType is defined on a trait, not an inherent impl)
107
108                    ctx.iterate_path_candidates(&ty, |item| {
109                        add_assoc_item(acc, item);
110                    });
111
112                    // Iterate assoc types separately
113                    ty.iterate_assoc_items(ctx.db, |item| {
114                        if let hir::AssocItem::TypeAlias(ty) = item {
115                            acc.add_type_alias(ctx, ty)
116                        }
117                        None::<()>
118                    });
119                }
120                hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => {
121                    // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
122                    for item in t.items(ctx.db) {
123                        add_assoc_item(acc, item);
124                    }
125                }
126                hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
127                    let ty = match resolution {
128                        hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
129                        hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
130                        _ => return,
131                    };
132
133                    ctx.iterate_path_candidates(&ty, |item| {
134                        add_assoc_item(acc, item);
135                    });
136                }
137                _ => (),
138            }
139        }
140        Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
141        Qualified::No => {
142            match location {
143                TypeLocation::TypeBound => {
144                    acc.add_nameref_keywords_with_colon(ctx);
145                    ctx.process_all_names(&mut |name, res, doc_aliases| {
146                        let add_resolution = match res {
147                            ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => {
148                                mac.is_fn_like(ctx.db)
149                            }
150                            ScopeDef::ModuleDef(
151                                hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_),
152                            ) => true,
153                            _ => false,
154                        };
155                        if add_resolution {
156                            acc.add_path_resolution(ctx, path_ctx, name, res, doc_aliases);
157                        }
158                    });
159                    return;
160                }
161                TypeLocation::GenericArg {
162                    args: Some(arg_list), of_trait: Some(trait_), ..
163                } => {
164                    if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() {
165                        let arg_idx = arg_list
166                            .generic_args()
167                            .filter(|arg| {
168                                arg.syntax().text_range().end()
169                                    < ctx.original_token.text_range().start()
170                            })
171                            .count();
172
173                        let n_required_params = trait_.type_or_const_param_count(ctx.sema.db, true);
174                        if arg_idx >= n_required_params {
175                            trait_.items_with_supertraits(ctx.sema.db).into_iter().for_each(|it| {
176                                if let hir::AssocItem::TypeAlias(alias) = it {
177                                    cov_mark::hit!(complete_assoc_type_in_generics_list);
178                                    acc.add_type_alias_with_eq(ctx, alias);
179                                }
180                            });
181
182                            let n_params = trait_.type_or_const_param_count(ctx.sema.db, false);
183                            if arg_idx >= n_params {
184                                return; // only show assoc types
185                            }
186                        }
187                    }
188                }
189                TypeLocation::ImplTrait => {
190                    acc.add_nameref_keywords_with_colon(ctx);
191                    ctx.process_all_names(&mut |name, def, doc_aliases| {
192                        let is_trait_or_module = matches!(
193                            def,
194                            ScopeDef::ModuleDef(
195                                hir::ModuleDef::Module(_) | hir::ModuleDef::Trait(_)
196                            )
197                        );
198                        if is_trait_or_module {
199                            acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases);
200                        }
201                    });
202                    return;
203                }
204                _ => {}
205            };
206
207            acc.add_nameref_keywords_with_colon(ctx);
208            acc.add_type_keywords(ctx);
209            ctx.process_all_names(&mut |name, def, doc_aliases| {
210                if scope_def_applicable(def) {
211                    acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases);
212                }
213            });
214        }
215    }
216}
217
218pub(crate) fn complete_ascribed_type(
219    acc: &mut Completions,
220    ctx: &CompletionContext<'_>,
221    path_ctx: &PathCompletionCtx<'_>,
222    ascription: &TypeAscriptionTarget,
223) -> Option<()> {
224    if !path_ctx.is_trivial_path() {
225        return None;
226    }
227    let ty = match ascription {
228        TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => {
229            ctx.sema.type_of_pat(pat.as_ref()?)
230        }
231        TypeAscriptionTarget::Const(exp) | TypeAscriptionTarget::RetType(exp) => {
232            ctx.sema.type_of_expr(exp.as_ref()?)
233        }
234    }?
235    .adjusted();
236    if !ty.is_unknown() {
237        let ty_string = ty.display_source_code(ctx.db, ctx.module.into(), true).ok()?;
238        acc.add(render_type_inference(ty_string, ctx));
239    }
240    None
241}