]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! Completion of names from the current scope in expression position. |
2 | ||
31ef2f64 | 3 | use hir::{ImportPathConfig, ScopeDef}; |
f2b60f7d | 4 | use syntax::ast; |
064997fb FG |
5 | |
6 | use crate::{ | |
f2b60f7d | 7 | completions::record::add_default_update, |
c620b35d | 8 | context::{BreakableKind, PathCompletionCtx, PathExprCtx, Qualified}, |
064997fb FG |
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, | |
c620b35d | 16 | expr_ctx: &PathExprCtx, |
064997fb | 17 | ) { |
c620b35d | 18 | let _p = tracing::span!(tracing::Level::INFO, "complete_expr_path").entered(); |
064997fb FG |
19 | if !ctx.qualifier_ctx.none() { |
20 | return; | |
21 | } | |
22 | ||
c620b35d | 23 | let &PathExprCtx { |
064997fb | 24 | in_block_expr, |
c620b35d | 25 | in_breakable, |
064997fb FG |
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 | ||
9c376795 | 67 | ctx.iterate_path_candidates(ty, |item| { |
064997fb FG |
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) { | |
fe692bf9 FG |
91 | acc.add_path_resolution( |
92 | ctx, | |
93 | path_ctx, | |
94 | name, | |
95 | def, | |
96 | ctx.doc_aliases_in_scope(def), | |
97 | ); | |
064997fb FG |
98 | } |
99 | } | |
100 | } | |
101 | hir::PathResolution::Def( | |
102 | def @ (hir::ModuleDef::Adt(_) | |
103 | | hir::ModuleDef::TypeAlias(_) | |
104 | | hir::ModuleDef::BuiltinType(_)), | |
105 | ) => { | |
106 | let ty = match def { | |
107 | hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), | |
108 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), | |
109 | hir::ModuleDef::BuiltinType(builtin) => { | |
110 | cov_mark::hit!(completes_primitive_assoc_const); | |
111 | builtin.ty(ctx.db) | |
112 | } | |
113 | _ => return, | |
114 | }; | |
115 | ||
116 | if let Some(hir::Adt::Enum(e)) = ty.as_adt() { | |
117 | cov_mark::hit!(completes_variant_through_alias); | |
118 | acc.add_enum_variants(ctx, path_ctx, e); | |
119 | } | |
120 | ||
121 | // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. | |
122 | // (where AssocType is defined on a trait, not an inherent impl) | |
123 | ||
124 | ctx.iterate_path_candidates(&ty, |item| { | |
125 | add_assoc_item(acc, item); | |
126 | }); | |
127 | ||
128 | // Iterate assoc types separately | |
129 | ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { | |
130 | if let hir::AssocItem::TypeAlias(ty) = item { | |
131 | acc.add_type_alias(ctx, ty) | |
132 | } | |
133 | None::<()> | |
134 | }); | |
135 | } | |
136 | hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => { | |
137 | // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`. | |
138 | for item in t.items(ctx.db) { | |
139 | add_assoc_item(acc, item); | |
140 | } | |
141 | } | |
142 | hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => { | |
143 | let ty = match resolution { | |
144 | hir::PathResolution::TypeParam(param) => param.ty(ctx.db), | |
145 | hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), | |
146 | _ => return, | |
147 | }; | |
148 | ||
149 | if let Some(hir::Adt::Enum(e)) = ty.as_adt() { | |
150 | cov_mark::hit!(completes_variant_through_self); | |
151 | acc.add_enum_variants(ctx, path_ctx, e); | |
152 | } | |
153 | ||
154 | ctx.iterate_path_candidates(&ty, |item| { | |
155 | add_assoc_item(acc, item); | |
156 | }); | |
157 | } | |
158 | _ => (), | |
159 | } | |
160 | } | |
161 | Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx), | |
162 | Qualified::No => { | |
163 | acc.add_nameref_keywords_with_colon(ctx); | |
164 | if let Some(adt) = | |
165 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) | |
166 | { | |
167 | let self_ty = (|| ctx.sema.to_def(impl_.as_ref()?)?.self_ty(ctx.db).as_adt())(); | |
168 | let complete_self = self_ty == Some(adt); | |
169 | ||
170 | match adt { | |
171 | hir::Adt::Struct(strukt) => { | |
172 | let path = ctx | |
173 | .module | |
31ef2f64 | 174 | .find_path( |
2b03887a FG |
175 | ctx.db, |
176 | hir::ModuleDef::from(strukt), | |
31ef2f64 FG |
177 | ImportPathConfig { |
178 | prefer_no_std: ctx.config.prefer_no_std, | |
179 | prefer_prelude: ctx.config.prefer_prelude, | |
180 | }, | |
2b03887a | 181 | ) |
064997fb FG |
182 | .filter(|it| it.len() > 1); |
183 | ||
184 | acc.add_struct_literal(ctx, path_ctx, strukt, path, None); | |
185 | ||
186 | if complete_self { | |
187 | acc.add_struct_literal( | |
188 | ctx, | |
189 | path_ctx, | |
190 | strukt, | |
191 | None, | |
192 | Some(hir::known::SELF_TYPE), | |
193 | ); | |
194 | } | |
195 | } | |
196 | hir::Adt::Union(un) => { | |
197 | let path = ctx | |
198 | .module | |
31ef2f64 | 199 | .find_path( |
2b03887a FG |
200 | ctx.db, |
201 | hir::ModuleDef::from(un), | |
31ef2f64 FG |
202 | ImportPathConfig { |
203 | prefer_no_std: ctx.config.prefer_no_std, | |
204 | prefer_prelude: ctx.config.prefer_prelude, | |
205 | }, | |
2b03887a | 206 | ) |
064997fb FG |
207 | .filter(|it| it.len() > 1); |
208 | ||
209 | acc.add_union_literal(ctx, un, path, None); | |
210 | if complete_self { | |
211 | acc.add_union_literal(ctx, un, None, Some(hir::known::SELF_TYPE)); | |
212 | } | |
213 | } | |
214 | hir::Adt::Enum(e) => { | |
215 | super::enum_variants_with_paths( | |
216 | acc, | |
217 | ctx, | |
218 | e, | |
219 | impl_, | |
220 | |acc, ctx, variant, path| { | |
221 | acc.add_qualified_enum_variant(ctx, path_ctx, variant, path) | |
222 | }, | |
223 | ); | |
224 | } | |
225 | } | |
226 | } | |
fe692bf9 | 227 | ctx.process_all_names(&mut |name, def, doc_aliases| match def { |
064997fb FG |
228 | ScopeDef::ModuleDef(hir::ModuleDef::Trait(t)) => { |
229 | let assocs = t.items_with_supertraits(ctx.db); | |
230 | match &*assocs { | |
231 | // traits with no assoc items are unusable as expressions since | |
232 | // there is no associated item path that can be constructed with them | |
233 | [] => (), | |
234 | // FIXME: Render the assoc item with the trait qualified | |
fe692bf9 | 235 | &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases), |
064997fb | 236 | // FIXME: Append `::` to the thing here, since a trait on its own won't work |
fe692bf9 | 237 | [..] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases), |
064997fb FG |
238 | } |
239 | } | |
fe692bf9 FG |
240 | _ if scope_def_applicable(def) => { |
241 | acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) | |
242 | } | |
064997fb FG |
243 | _ => (), |
244 | }); | |
245 | ||
f2b60f7d FG |
246 | match is_func_update { |
247 | Some(record_expr) => { | |
248 | let ty = ctx.sema.type_of_expr(&ast::Expr::RecordExpr(record_expr.clone())); | |
064997fb | 249 | |
f2b60f7d FG |
250 | match ty.as_ref().and_then(|t| t.original.as_adt()) { |
251 | Some(hir::Adt::Union(_)) => (), | |
252 | _ => { | |
253 | cov_mark::hit!(functional_update); | |
254 | let missing_fields = | |
255 | ctx.sema.record_literal_missing_fields(record_expr); | |
256 | if !missing_fields.is_empty() { | |
257 | add_default_update(acc, ctx, ty); | |
258 | } | |
259 | } | |
260 | }; | |
064997fb | 261 | } |
f2b60f7d FG |
262 | None => { |
263 | let mut add_keyword = |kw, snippet| { | |
264 | acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet) | |
265 | }; | |
064997fb | 266 | |
f2b60f7d FG |
267 | if !in_block_expr { |
268 | add_keyword("unsafe", "unsafe {\n $0\n}"); | |
269 | } | |
270 | add_keyword("match", "match $1 {\n $0\n}"); | |
271 | add_keyword("while", "while $1 {\n $0\n}"); | |
272 | add_keyword("while let", "while let $1 = $2 {\n $0\n}"); | |
273 | add_keyword("loop", "loop {\n $0\n}"); | |
274 | if in_match_guard { | |
275 | add_keyword("if", "if $0"); | |
276 | } else { | |
277 | add_keyword("if", "if $1 {\n $0\n}"); | |
278 | } | |
279 | add_keyword("if let", "if let $1 = $2 {\n $0\n}"); | |
280 | add_keyword("for", "for $1 in $2 {\n $0\n}"); | |
281 | add_keyword("true", "true"); | |
282 | add_keyword("false", "false"); | |
064997fb | 283 | |
f2b60f7d FG |
284 | if in_condition || in_block_expr { |
285 | add_keyword("let", "let"); | |
286 | } | |
064997fb | 287 | |
f2b60f7d FG |
288 | if after_if_expr { |
289 | add_keyword("else", "else {\n $0\n}"); | |
290 | add_keyword("else if", "else if $1 {\n $0\n}"); | |
291 | } | |
064997fb | 292 | |
f2b60f7d FG |
293 | if wants_mut_token { |
294 | add_keyword("mut", "mut "); | |
295 | } | |
296 | ||
c620b35d | 297 | if in_breakable != BreakableKind::None { |
f2b60f7d FG |
298 | if in_block_expr { |
299 | add_keyword("continue", "continue;"); | |
300 | add_keyword("break", "break;"); | |
301 | } else { | |
302 | add_keyword("continue", "continue"); | |
303 | add_keyword("break", "break"); | |
304 | } | |
064997fb | 305 | } |
064997fb | 306 | |
f2b60f7d FG |
307 | if let Some(ret_ty) = innermost_ret_ty { |
308 | add_keyword( | |
309 | "return", | |
310 | match (ret_ty.is_unit(), in_block_expr) { | |
311 | (true, true) => { | |
312 | cov_mark::hit!(return_unit_block); | |
313 | "return;" | |
314 | } | |
315 | (true, false) => { | |
316 | cov_mark::hit!(return_unit_no_block); | |
317 | "return" | |
318 | } | |
319 | (false, true) => { | |
320 | cov_mark::hit!(return_value_block); | |
321 | "return $0;" | |
322 | } | |
323 | (false, false) => { | |
324 | cov_mark::hit!(return_value_no_block); | |
325 | "return $0" | |
326 | } | |
327 | }, | |
328 | ); | |
329 | } | |
064997fb FG |
330 | } |
331 | } | |
332 | } | |
333 | } | |
334 | } | |
c620b35d FG |
335 | |
336 | pub(crate) fn complete_expr(acc: &mut Completions, ctx: &CompletionContext<'_>) { | |
337 | let _p = tracing::span!(tracing::Level::INFO, "complete_expr").entered(); | |
338 | ||
339 | if !ctx.config.enable_term_search { | |
340 | return; | |
341 | } | |
342 | ||
343 | if !ctx.qualifier_ctx.none() { | |
344 | return; | |
345 | } | |
346 | ||
347 | if let Some(ty) = &ctx.expected_type { | |
348 | // Ignore unit types as they are not very interesting | |
349 | if ty.is_unit() || ty.is_unknown() { | |
350 | return; | |
351 | } | |
352 | ||
353 | let term_search_ctx = hir::term_search::TermSearchCtx { | |
354 | sema: &ctx.sema, | |
355 | scope: &ctx.scope, | |
356 | goal: ty.clone(), | |
357 | config: hir::term_search::TermSearchConfig { | |
358 | enable_borrowcheck: false, | |
359 | many_alternatives_threshold: 1, | |
31ef2f64 | 360 | fuel: 200, |
c620b35d FG |
361 | }, |
362 | }; | |
363 | let exprs = hir::term_search::term_search(&term_search_ctx); | |
364 | for expr in exprs { | |
365 | // Expand method calls | |
366 | match expr { | |
367 | hir::term_search::Expr::Method { func, generics, target, params } | |
368 | if target.is_many() => | |
369 | { | |
370 | let target_ty = target.ty(ctx.db); | |
371 | let term_search_ctx = | |
372 | hir::term_search::TermSearchCtx { goal: target_ty, ..term_search_ctx }; | |
373 | let target_exprs = hir::term_search::term_search(&term_search_ctx); | |
374 | ||
375 | for expr in target_exprs { | |
376 | let expanded_expr = hir::term_search::Expr::Method { | |
377 | func, | |
378 | generics: generics.clone(), | |
379 | target: Box::new(expr), | |
380 | params: params.clone(), | |
381 | }; | |
382 | ||
383 | acc.add_expr(ctx, &expanded_expr) | |
384 | } | |
385 | } | |
386 | _ => acc.add_expr(ctx, &expr), | |
387 | } | |
388 | } | |
389 | } | |
390 | } |