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