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