]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
Rollup merge of #99742 - sigaloid:master, r=thomcc
[rust.git] / src / tools / rust-analyzer / crates / ide-completion / src / completions / expr.rs
1 //! Completion of names from the current scope in expression position.
2
3 use hir::ScopeDef;
4 use syntax::ast;
5
6 use crate::{
7     completions::record::add_default_update,
8     context::{ExprCtx, PathCompletionCtx, Qualified},
9     CompletionContext, Completions,
10 };
11
12 pub(crate) fn complete_expr_path(
13     acc: &mut Completions,
14     ctx: &CompletionContext<'_>,
15     path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
16     expr_ctx: &ExprCtx,
17 ) {
18     let _p = profile::span("complete_expr_path");
19     if !ctx.qualifier_ctx.none() {
20         return;
21     }
22
23     let &ExprCtx {
24         in_block_expr,
25         in_loop_body,
26         after_if_expr,
27         in_condition,
28         incomplete_let,
29         ref ref_expr_parent,
30         ref is_func_update,
31         ref innermost_ret_ty,
32         ref impl_,
33         in_match_guard,
34         ..
35     } = expr_ctx;
36
37     let wants_mut_token =
38         ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false);
39
40     let scope_def_applicable = |def| match def {
41         ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => false,
42         ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
43         _ => true,
44     };
45
46     let add_assoc_item = |acc: &mut Completions, item| match item {
47         hir::AssocItem::Function(func) => acc.add_function(ctx, path_ctx, func, None),
48         hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
49         hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
50     };
51
52     match qualified {
53         Qualified::TypeAnchor { ty: None, trait_: None } => ctx
54             .traits_in_scope()
55             .iter()
56             .flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db))
57             .for_each(|item| add_assoc_item(acc, item)),
58         Qualified::TypeAnchor { trait_: Some(trait_), .. } => {
59             trait_.items(ctx.sema.db).into_iter().for_each(|item| add_assoc_item(acc, item))
60         }
61         Qualified::TypeAnchor { ty: Some(ty), trait_: None } => {
62             if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
63                 cov_mark::hit!(completes_variant_through_alias);
64                 acc.add_enum_variants(ctx, path_ctx, e);
65             }
66
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, ctx.krate, |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                 None::<()>
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);
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) => {
104                             cov_mark::hit!(completes_primitive_assoc_const);
105                             builtin.ty(ctx.db)
106                         }
107                         _ => return,
108                     };
109
110                     if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
111                         cov_mark::hit!(completes_variant_through_alias);
112                         acc.add_enum_variants(ctx, path_ctx, e);
113                     }
114
115                     // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
116                     // (where AssocType is defined on a trait, not an inherent impl)
117
118                     ctx.iterate_path_candidates(&ty, |item| {
119                         add_assoc_item(acc, item);
120                     });
121
122                     // Iterate assoc types separately
123                     ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
124                         if let hir::AssocItem::TypeAlias(ty) = item {
125                             acc.add_type_alias(ctx, ty)
126                         }
127                         None::<()>
128                     });
129                 }
130                 hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => {
131                     // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
132                     for item in t.items(ctx.db) {
133                         add_assoc_item(acc, item);
134                     }
135                 }
136                 hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
137                     let ty = match resolution {
138                         hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
139                         hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
140                         _ => return,
141                     };
142
143                     if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
144                         cov_mark::hit!(completes_variant_through_self);
145                         acc.add_enum_variants(ctx, path_ctx, e);
146                     }
147
148                     ctx.iterate_path_candidates(&ty, |item| {
149                         add_assoc_item(acc, item);
150                     });
151                 }
152                 _ => (),
153             }
154         }
155         Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
156         Qualified::No => {
157             acc.add_nameref_keywords_with_colon(ctx);
158             if let Some(adt) =
159                 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
160             {
161                 let self_ty = (|| ctx.sema.to_def(impl_.as_ref()?)?.self_ty(ctx.db).as_adt())();
162                 let complete_self = self_ty == Some(adt);
163
164                 match adt {
165                     hir::Adt::Struct(strukt) => {
166                         let path = ctx
167                             .module
168                             .find_use_path(ctx.db, hir::ModuleDef::from(strukt))
169                             .filter(|it| it.len() > 1);
170
171                         acc.add_struct_literal(ctx, path_ctx, strukt, path, None);
172
173                         if complete_self {
174                             acc.add_struct_literal(
175                                 ctx,
176                                 path_ctx,
177                                 strukt,
178                                 None,
179                                 Some(hir::known::SELF_TYPE),
180                             );
181                         }
182                     }
183                     hir::Adt::Union(un) => {
184                         let path = ctx
185                             .module
186                             .find_use_path(ctx.db, hir::ModuleDef::from(un))
187                             .filter(|it| it.len() > 1);
188
189                         acc.add_union_literal(ctx, un, path, None);
190                         if complete_self {
191                             acc.add_union_literal(ctx, un, None, Some(hir::known::SELF_TYPE));
192                         }
193                     }
194                     hir::Adt::Enum(e) => {
195                         super::enum_variants_with_paths(
196                             acc,
197                             ctx,
198                             e,
199                             impl_,
200                             |acc, ctx, variant, path| {
201                                 acc.add_qualified_enum_variant(ctx, path_ctx, variant, path)
202                             },
203                         );
204                     }
205                 }
206             }
207             ctx.process_all_names(&mut |name, def| match def {
208                 ScopeDef::ModuleDef(hir::ModuleDef::Trait(t)) => {
209                     let assocs = t.items_with_supertraits(ctx.db);
210                     match &*assocs {
211                         // traits with no assoc items are unusable as expressions since
212                         // there is no associated item path that can be constructed with them
213                         [] => (),
214                         // FIXME: Render the assoc item with the trait qualified
215                         &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def),
216                         // FIXME: Append `::` to the thing here, since a trait on its own won't work
217                         [..] => acc.add_path_resolution(ctx, path_ctx, name, def),
218                     }
219                 }
220                 _ if scope_def_applicable(def) => acc.add_path_resolution(ctx, path_ctx, name, def),
221                 _ => (),
222             });
223
224             match is_func_update {
225                 Some(record_expr) => {
226                     let ty = ctx.sema.type_of_expr(&ast::Expr::RecordExpr(record_expr.clone()));
227
228                     match ty.as_ref().and_then(|t| t.original.as_adt()) {
229                         Some(hir::Adt::Union(_)) => (),
230                         _ => {
231                             cov_mark::hit!(functional_update);
232                             let missing_fields =
233                                 ctx.sema.record_literal_missing_fields(record_expr);
234                             if !missing_fields.is_empty() {
235                                 add_default_update(acc, ctx, ty);
236                             }
237                         }
238                     };
239                 }
240                 None => {
241                     let mut add_keyword = |kw, snippet| {
242                         acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet)
243                     };
244
245                     if !in_block_expr {
246                         add_keyword("unsafe", "unsafe {\n    $0\n}");
247                     }
248                     add_keyword("match", "match $1 {\n    $0\n}");
249                     add_keyword("while", "while $1 {\n    $0\n}");
250                     add_keyword("while let", "while let $1 = $2 {\n    $0\n}");
251                     add_keyword("loop", "loop {\n    $0\n}");
252                     if in_match_guard {
253                         add_keyword("if", "if $0");
254                     } else {
255                         add_keyword("if", "if $1 {\n    $0\n}");
256                     }
257                     add_keyword("if let", "if let $1 = $2 {\n    $0\n}");
258                     add_keyword("for", "for $1 in $2 {\n    $0\n}");
259                     add_keyword("true", "true");
260                     add_keyword("false", "false");
261
262                     if in_condition || in_block_expr {
263                         add_keyword("let", "let");
264                     }
265
266                     if after_if_expr {
267                         add_keyword("else", "else {\n    $0\n}");
268                         add_keyword("else if", "else if $1 {\n    $0\n}");
269                     }
270
271                     if wants_mut_token {
272                         add_keyword("mut", "mut ");
273                     }
274
275                     if in_loop_body {
276                         if in_block_expr {
277                             add_keyword("continue", "continue;");
278                             add_keyword("break", "break;");
279                         } else {
280                             add_keyword("continue", "continue");
281                             add_keyword("break", "break");
282                         }
283                     }
284
285                     if let Some(ty) = innermost_ret_ty {
286                         add_keyword(
287                             "return",
288                             match (in_block_expr, ty.is_unit()) {
289                                 (true, true) => "return ;",
290                                 (true, false) => "return;",
291                                 (false, true) => "return $0",
292                                 (false, false) => "return",
293                             },
294                         );
295                     }
296                 }
297             }
298         }
299     }
300 }