]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
Rollup merge of #104199 - SarthakSingh31:issue-97417-1, r=cjgillot
[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(
169                                 ctx.db,
170                                 hir::ModuleDef::from(strukt),
171                                 ctx.config.prefer_no_std,
172                             )
173                             .filter(|it| it.len() > 1);
174
175                         acc.add_struct_literal(ctx, path_ctx, strukt, path, None);
176
177                         if complete_self {
178                             acc.add_struct_literal(
179                                 ctx,
180                                 path_ctx,
181                                 strukt,
182                                 None,
183                                 Some(hir::known::SELF_TYPE),
184                             );
185                         }
186                     }
187                     hir::Adt::Union(un) => {
188                         let path = ctx
189                             .module
190                             .find_use_path(
191                                 ctx.db,
192                                 hir::ModuleDef::from(un),
193                                 ctx.config.prefer_no_std,
194                             )
195                             .filter(|it| it.len() > 1);
196
197                         acc.add_union_literal(ctx, un, path, None);
198                         if complete_self {
199                             acc.add_union_literal(ctx, un, None, Some(hir::known::SELF_TYPE));
200                         }
201                     }
202                     hir::Adt::Enum(e) => {
203                         super::enum_variants_with_paths(
204                             acc,
205                             ctx,
206                             e,
207                             impl_,
208                             |acc, ctx, variant, path| {
209                                 acc.add_qualified_enum_variant(ctx, path_ctx, variant, path)
210                             },
211                         );
212                     }
213                 }
214             }
215             ctx.process_all_names(&mut |name, def| match def {
216                 ScopeDef::ModuleDef(hir::ModuleDef::Trait(t)) => {
217                     let assocs = t.items_with_supertraits(ctx.db);
218                     match &*assocs {
219                         // traits with no assoc items are unusable as expressions since
220                         // there is no associated item path that can be constructed with them
221                         [] => (),
222                         // FIXME: Render the assoc item with the trait qualified
223                         &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def),
224                         // FIXME: Append `::` to the thing here, since a trait on its own won't work
225                         [..] => acc.add_path_resolution(ctx, path_ctx, name, def),
226                     }
227                 }
228                 _ if scope_def_applicable(def) => acc.add_path_resolution(ctx, path_ctx, name, def),
229                 _ => (),
230             });
231
232             match is_func_update {
233                 Some(record_expr) => {
234                     let ty = ctx.sema.type_of_expr(&ast::Expr::RecordExpr(record_expr.clone()));
235
236                     match ty.as_ref().and_then(|t| t.original.as_adt()) {
237                         Some(hir::Adt::Union(_)) => (),
238                         _ => {
239                             cov_mark::hit!(functional_update);
240                             let missing_fields =
241                                 ctx.sema.record_literal_missing_fields(record_expr);
242                             if !missing_fields.is_empty() {
243                                 add_default_update(acc, ctx, ty);
244                             }
245                         }
246                     };
247                 }
248                 None => {
249                     let mut add_keyword = |kw, snippet| {
250                         acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet)
251                     };
252
253                     if !in_block_expr {
254                         add_keyword("unsafe", "unsafe {\n    $0\n}");
255                     }
256                     add_keyword("match", "match $1 {\n    $0\n}");
257                     add_keyword("while", "while $1 {\n    $0\n}");
258                     add_keyword("while let", "while let $1 = $2 {\n    $0\n}");
259                     add_keyword("loop", "loop {\n    $0\n}");
260                     if in_match_guard {
261                         add_keyword("if", "if $0");
262                     } else {
263                         add_keyword("if", "if $1 {\n    $0\n}");
264                     }
265                     add_keyword("if let", "if let $1 = $2 {\n    $0\n}");
266                     add_keyword("for", "for $1 in $2 {\n    $0\n}");
267                     add_keyword("true", "true");
268                     add_keyword("false", "false");
269
270                     if in_condition || in_block_expr {
271                         add_keyword("let", "let");
272                     }
273
274                     if after_if_expr {
275                         add_keyword("else", "else {\n    $0\n}");
276                         add_keyword("else if", "else if $1 {\n    $0\n}");
277                     }
278
279                     if wants_mut_token {
280                         add_keyword("mut", "mut ");
281                     }
282
283                     if in_loop_body {
284                         if in_block_expr {
285                             add_keyword("continue", "continue;");
286                             add_keyword("break", "break;");
287                         } else {
288                             add_keyword("continue", "continue");
289                             add_keyword("break", "break");
290                         }
291                     }
292
293                     if let Some(ret_ty) = innermost_ret_ty {
294                         add_keyword(
295                             "return",
296                             match (ret_ty.is_unit(), in_block_expr) {
297                                 (true, true) => {
298                                     cov_mark::hit!(return_unit_block);
299                                     "return;"
300                                 }
301                                 (true, false) => {
302                                     cov_mark::hit!(return_unit_no_block);
303                                     "return"
304                                 }
305                                 (false, true) => {
306                                     cov_mark::hit!(return_value_block);
307                                     "return $0;"
308                                 }
309                                 (false, false) => {
310                                     cov_mark::hit!(return_value_no_block);
311                                     "return $0"
312                                 }
313                             },
314                         );
315                     }
316                 }
317             }
318         }
319     }
320 }