]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! `render` module provides utilities for rendering completion suggestions |
2 | //! into code pieces that will be presented to user. | |
3 | ||
4 | pub(crate) mod macro_; | |
5 | pub(crate) mod function; | |
6 | pub(crate) mod const_; | |
7 | pub(crate) mod pattern; | |
8 | pub(crate) mod type_alias; | |
9 | pub(crate) mod variant; | |
10 | pub(crate) mod union_literal; | |
11 | pub(crate) mod literal; | |
12 | ||
13 | use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef}; | |
14 | use ide_db::{ | |
15 | helpers::item_name, imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind, | |
16 | }; | |
17 | use syntax::{AstNode, SmolStr, SyntaxKind, TextRange}; | |
18 | ||
19 | use crate::{ | |
20 | context::{DotAccess, PathCompletionCtx, PathKind, PatternContext}, | |
21 | item::{Builder, CompletionRelevanceTypeMatch}, | |
22 | render::{ | |
23 | function::render_fn, | |
24 | literal::render_variant_lit, | |
25 | macro_::{render_macro, render_macro_pat}, | |
26 | }, | |
27 | CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, | |
28 | }; | |
29 | /// Interface for data and methods required for items rendering. | |
30 | #[derive(Debug, Clone)] | |
31 | pub(crate) struct RenderContext<'a> { | |
32 | completion: &'a CompletionContext<'a>, | |
33 | is_private_editable: bool, | |
34 | import_to_add: Option<LocatedImport>, | |
35 | } | |
36 | ||
37 | impl<'a> RenderContext<'a> { | |
38 | pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> { | |
39 | RenderContext { completion, is_private_editable: false, import_to_add: None } | |
40 | } | |
41 | ||
42 | pub(crate) fn private_editable(mut self, private_editable: bool) -> Self { | |
43 | self.is_private_editable = private_editable; | |
44 | self | |
45 | } | |
46 | ||
47 | pub(crate) fn import_to_add(mut self, import_to_add: Option<LocatedImport>) -> Self { | |
48 | self.import_to_add = import_to_add; | |
49 | self | |
50 | } | |
51 | ||
52 | fn snippet_cap(&self) -> Option<SnippetCap> { | |
53 | self.completion.config.snippet_cap | |
54 | } | |
55 | ||
56 | fn db(&self) -> &'a RootDatabase { | |
57 | self.completion.db | |
58 | } | |
59 | ||
60 | fn source_range(&self) -> TextRange { | |
61 | self.completion.source_range() | |
62 | } | |
63 | ||
64 | fn completion_relevance(&self) -> CompletionRelevance { | |
65 | CompletionRelevance { | |
66 | is_private_editable: self.is_private_editable, | |
67 | requires_import: self.import_to_add.is_some(), | |
68 | ..Default::default() | |
69 | } | |
70 | } | |
71 | ||
72 | fn is_immediately_after_macro_bang(&self) -> bool { | |
73 | self.completion.token.kind() == SyntaxKind::BANG | |
74 | && self | |
75 | .completion | |
76 | .token | |
77 | .parent() | |
78 | .map_or(false, |it| it.kind() == SyntaxKind::MACRO_CALL) | |
79 | } | |
80 | ||
81 | fn is_deprecated(&self, def: impl HasAttrs) -> bool { | |
82 | let attrs = def.attrs(self.db()); | |
83 | attrs.by_key("deprecated").exists() | |
84 | } | |
85 | ||
86 | fn is_deprecated_assoc_item(&self, as_assoc_item: impl AsAssocItem) -> bool { | |
87 | let db = self.db(); | |
88 | let assoc = match as_assoc_item.as_assoc_item(db) { | |
89 | Some(assoc) => assoc, | |
90 | None => return false, | |
91 | }; | |
92 | ||
93 | let is_assoc_deprecated = match assoc { | |
94 | hir::AssocItem::Function(it) => self.is_deprecated(it), | |
95 | hir::AssocItem::Const(it) => self.is_deprecated(it), | |
96 | hir::AssocItem::TypeAlias(it) => self.is_deprecated(it), | |
97 | }; | |
98 | is_assoc_deprecated | |
99 | || assoc | |
100 | .containing_trait_or_trait_impl(db) | |
101 | .map(|trait_| self.is_deprecated(trait_)) | |
102 | .unwrap_or(false) | |
103 | } | |
104 | ||
105 | // FIXME: remove this | |
106 | fn docs(&self, def: impl HasAttrs) -> Option<hir::Documentation> { | |
107 | def.docs(self.db()) | |
108 | } | |
109 | } | |
110 | ||
111 | pub(crate) fn render_field( | |
112 | ctx: RenderContext<'_>, | |
113 | dot_access: &DotAccess, | |
114 | receiver: Option<hir::Name>, | |
115 | field: hir::Field, | |
116 | ty: &hir::Type, | |
117 | ) -> CompletionItem { | |
118 | let is_deprecated = ctx.is_deprecated(field); | |
119 | let name = field.name(ctx.db()); | |
f2b60f7d | 120 | let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); |
064997fb FG |
121 | let mut item = CompletionItem::new( |
122 | SymbolKind::Field, | |
123 | ctx.source_range(), | |
124 | field_with_receiver(receiver.as_ref(), &name), | |
125 | ); | |
126 | item.set_relevance(CompletionRelevance { | |
127 | type_match: compute_type_match(ctx.completion, ty), | |
128 | exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()), | |
129 | ..CompletionRelevance::default() | |
130 | }); | |
131 | item.detail(ty.display(ctx.db()).to_string()) | |
132 | .set_documentation(field.docs(ctx.db())) | |
133 | .set_deprecated(is_deprecated) | |
9c376795 | 134 | .lookup_by(name); |
064997fb FG |
135 | item.insert_text(field_with_receiver(receiver.as_ref(), &escaped_name)); |
136 | if let Some(receiver) = &dot_access.receiver { | |
137 | if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) { | |
138 | if let Some(ref_match) = compute_ref_match(ctx.completion, ty) { | |
139 | item.ref_match(ref_match, original.syntax().text_range().start()); | |
140 | } | |
141 | } | |
142 | } | |
143 | item.build() | |
144 | } | |
145 | ||
146 | fn field_with_receiver(receiver: Option<&hir::Name>, field_name: &str) -> SmolStr { | |
9c376795 | 147 | receiver.map_or_else(|| field_name.into(), |receiver| format!("{receiver}.{field_name}").into()) |
064997fb FG |
148 | } |
149 | ||
150 | pub(crate) fn render_tuple_field( | |
151 | ctx: RenderContext<'_>, | |
152 | receiver: Option<hir::Name>, | |
153 | field: usize, | |
154 | ty: &hir::Type, | |
155 | ) -> CompletionItem { | |
156 | let mut item = CompletionItem::new( | |
157 | SymbolKind::Field, | |
158 | ctx.source_range(), | |
159 | field_with_receiver(receiver.as_ref(), &field.to_string()), | |
160 | ); | |
161 | item.detail(ty.display(ctx.db()).to_string()).lookup_by(field.to_string()); | |
162 | item.build() | |
163 | } | |
164 | ||
165 | pub(crate) fn render_type_inference( | |
166 | ty_string: String, | |
167 | ctx: &CompletionContext<'_>, | |
168 | ) -> CompletionItem { | |
169 | let mut builder = | |
170 | CompletionItem::new(CompletionItemKind::InferredType, ctx.source_range(), ty_string); | |
171 | builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() }); | |
172 | builder.build() | |
173 | } | |
174 | ||
175 | pub(crate) fn render_path_resolution( | |
176 | ctx: RenderContext<'_>, | |
177 | path_ctx: &PathCompletionCtx, | |
178 | local_name: hir::Name, | |
179 | resolution: ScopeDef, | |
180 | ) -> Builder { | |
181 | render_resolution_path(ctx, path_ctx, local_name, None, resolution) | |
182 | } | |
183 | ||
184 | pub(crate) fn render_pattern_resolution( | |
185 | ctx: RenderContext<'_>, | |
186 | pattern_ctx: &PatternContext, | |
187 | local_name: hir::Name, | |
188 | resolution: ScopeDef, | |
189 | ) -> Builder { | |
190 | render_resolution_pat(ctx, pattern_ctx, local_name, None, resolution) | |
191 | } | |
192 | ||
193 | pub(crate) fn render_resolution_with_import( | |
194 | ctx: RenderContext<'_>, | |
195 | path_ctx: &PathCompletionCtx, | |
196 | import_edit: LocatedImport, | |
197 | ) -> Option<Builder> { | |
198 | let resolution = ScopeDef::from(import_edit.original_item); | |
199 | let local_name = scope_def_to_name(resolution, &ctx, &import_edit)?; | |
200 | ||
201 | Some(render_resolution_path(ctx, path_ctx, local_name, Some(import_edit), resolution)) | |
202 | } | |
203 | ||
204 | pub(crate) fn render_resolution_with_import_pat( | |
205 | ctx: RenderContext<'_>, | |
206 | pattern_ctx: &PatternContext, | |
207 | import_edit: LocatedImport, | |
208 | ) -> Option<Builder> { | |
209 | let resolution = ScopeDef::from(import_edit.original_item); | |
210 | let local_name = scope_def_to_name(resolution, &ctx, &import_edit)?; | |
211 | Some(render_resolution_pat(ctx, pattern_ctx, local_name, Some(import_edit), resolution)) | |
212 | } | |
213 | ||
214 | fn scope_def_to_name( | |
215 | resolution: ScopeDef, | |
216 | ctx: &RenderContext<'_>, | |
217 | import_edit: &LocatedImport, | |
218 | ) -> Option<hir::Name> { | |
219 | Some(match resolution { | |
220 | ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db), | |
221 | ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?, | |
222 | ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db), | |
223 | _ => item_name(ctx.db(), import_edit.original_item)?, | |
224 | }) | |
225 | } | |
226 | ||
227 | fn render_resolution_pat( | |
228 | ctx: RenderContext<'_>, | |
229 | pattern_ctx: &PatternContext, | |
230 | local_name: hir::Name, | |
231 | import_to_add: Option<LocatedImport>, | |
232 | resolution: ScopeDef, | |
233 | ) -> Builder { | |
234 | let _p = profile::span("render_resolution"); | |
235 | use hir::ModuleDef::*; | |
236 | ||
237 | match resolution { | |
238 | ScopeDef::ModuleDef(Macro(mac)) => { | |
239 | let ctx = ctx.import_to_add(import_to_add); | |
240 | return render_macro_pat(ctx, pattern_ctx, local_name, mac); | |
241 | } | |
242 | _ => (), | |
243 | } | |
244 | ||
245 | render_resolution_simple_(ctx, &local_name, import_to_add, resolution) | |
246 | } | |
247 | ||
248 | fn render_resolution_path( | |
249 | ctx: RenderContext<'_>, | |
250 | path_ctx: &PathCompletionCtx, | |
251 | local_name: hir::Name, | |
252 | import_to_add: Option<LocatedImport>, | |
253 | resolution: ScopeDef, | |
254 | ) -> Builder { | |
255 | let _p = profile::span("render_resolution"); | |
256 | use hir::ModuleDef::*; | |
257 | ||
258 | match resolution { | |
259 | ScopeDef::ModuleDef(Macro(mac)) => { | |
260 | let ctx = ctx.import_to_add(import_to_add); | |
261 | return render_macro(ctx, path_ctx, local_name, mac); | |
262 | } | |
263 | ScopeDef::ModuleDef(Function(func)) => { | |
264 | let ctx = ctx.import_to_add(import_to_add); | |
265 | return render_fn(ctx, path_ctx, Some(local_name), func); | |
266 | } | |
267 | ScopeDef::ModuleDef(Variant(var)) => { | |
268 | let ctx = ctx.clone().import_to_add(import_to_add.clone()); | |
269 | if let Some(item) = | |
270 | render_variant_lit(ctx, path_ctx, Some(local_name.clone()), var, None) | |
271 | { | |
272 | return item; | |
273 | } | |
274 | } | |
275 | _ => (), | |
276 | } | |
277 | ||
278 | let completion = ctx.completion; | |
279 | let cap = ctx.snippet_cap(); | |
280 | let db = completion.db; | |
281 | let config = completion.config; | |
282 | ||
283 | let name = local_name.to_smol_str(); | |
284 | let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution); | |
f2b60f7d FG |
285 | if local_name.is_escaped() { |
286 | item.insert_text(local_name.to_smol_str()); | |
064997fb FG |
287 | } |
288 | // Add `<>` for generic types | |
289 | let type_path_no_ty_args = matches!( | |
290 | path_ctx, | |
291 | PathCompletionCtx { kind: PathKind::Type { .. }, has_type_args: false, .. } | |
292 | ) && config.callable.is_some(); | |
293 | if type_path_no_ty_args { | |
294 | if let Some(cap) = cap { | |
295 | let has_non_default_type_params = match resolution { | |
296 | ScopeDef::ModuleDef(hir::ModuleDef::Adt(it)) => it.has_non_default_type_params(db), | |
297 | ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(it)) => { | |
298 | it.has_non_default_type_params(db) | |
299 | } | |
300 | _ => false, | |
301 | }; | |
302 | ||
303 | if has_non_default_type_params { | |
304 | cov_mark::hit!(inserts_angle_brackets_for_generics); | |
305 | item.lookup_by(name.clone()) | |
306 | .label(SmolStr::from_iter([&name, "<…>"])) | |
307 | .trigger_call_info() | |
9c376795 | 308 | .insert_snippet(cap, format!("{local_name}<$0>")); |
064997fb FG |
309 | } |
310 | } | |
311 | } | |
312 | if let ScopeDef::Local(local) = resolution { | |
313 | let ty = local.ty(db); | |
314 | if !ty.is_unknown() { | |
315 | item.detail(ty.display(db).to_string()); | |
316 | } | |
317 | ||
318 | item.set_relevance(CompletionRelevance { | |
319 | type_match: compute_type_match(completion, &ty), | |
320 | exact_name_match: compute_exact_name_match(completion, &name), | |
321 | is_local: true, | |
322 | ..CompletionRelevance::default() | |
323 | }); | |
324 | ||
f2b60f7d | 325 | path_ref_match(completion, path_ctx, &ty, &mut item); |
064997fb FG |
326 | }; |
327 | item | |
328 | } | |
329 | ||
330 | fn render_resolution_simple_( | |
331 | ctx: RenderContext<'_>, | |
332 | local_name: &hir::Name, | |
333 | import_to_add: Option<LocatedImport>, | |
334 | resolution: ScopeDef, | |
335 | ) -> Builder { | |
336 | let _p = profile::span("render_resolution"); | |
337 | ||
338 | let db = ctx.db(); | |
339 | let ctx = ctx.import_to_add(import_to_add); | |
340 | let kind = res_to_kind(resolution); | |
341 | ||
f2b60f7d FG |
342 | let mut item = |
343 | CompletionItem::new(kind, ctx.source_range(), local_name.unescaped().to_smol_str()); | |
064997fb FG |
344 | item.set_relevance(ctx.completion_relevance()) |
345 | .set_documentation(scope_def_docs(db, resolution)) | |
346 | .set_deprecated(scope_def_is_deprecated(&ctx, resolution)); | |
347 | ||
348 | if let Some(import_to_add) = ctx.import_to_add { | |
349 | item.add_import(import_to_add); | |
350 | } | |
351 | item | |
352 | } | |
353 | ||
354 | fn res_to_kind(resolution: ScopeDef) -> CompletionItemKind { | |
355 | use hir::ModuleDef::*; | |
356 | match resolution { | |
357 | ScopeDef::Unknown => CompletionItemKind::UnresolvedReference, | |
358 | ScopeDef::ModuleDef(Function(_)) => CompletionItemKind::SymbolKind(SymbolKind::Function), | |
359 | ScopeDef::ModuleDef(Variant(_)) => CompletionItemKind::SymbolKind(SymbolKind::Variant), | |
360 | ScopeDef::ModuleDef(Macro(_)) => CompletionItemKind::SymbolKind(SymbolKind::Macro), | |
361 | ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module), | |
362 | ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt { | |
363 | hir::Adt::Struct(_) => SymbolKind::Struct, | |
364 | hir::Adt::Union(_) => SymbolKind::Union, | |
365 | hir::Adt::Enum(_) => SymbolKind::Enum, | |
366 | }), | |
367 | ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), | |
368 | ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static), | |
369 | ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait), | |
370 | ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias), | |
371 | ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, | |
372 | ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { | |
373 | hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam, | |
374 | hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, | |
375 | hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam, | |
376 | }), | |
377 | ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), | |
378 | ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label), | |
379 | ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => { | |
380 | CompletionItemKind::SymbolKind(SymbolKind::SelfParam) | |
381 | } | |
382 | } | |
383 | } | |
384 | ||
385 | fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<hir::Documentation> { | |
386 | use hir::ModuleDef::*; | |
387 | match resolution { | |
388 | ScopeDef::ModuleDef(Module(it)) => it.docs(db), | |
389 | ScopeDef::ModuleDef(Adt(it)) => it.docs(db), | |
390 | ScopeDef::ModuleDef(Variant(it)) => it.docs(db), | |
391 | ScopeDef::ModuleDef(Const(it)) => it.docs(db), | |
392 | ScopeDef::ModuleDef(Static(it)) => it.docs(db), | |
393 | ScopeDef::ModuleDef(Trait(it)) => it.docs(db), | |
394 | ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(db), | |
395 | _ => None, | |
396 | } | |
397 | } | |
398 | ||
399 | fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> bool { | |
400 | match resolution { | |
401 | ScopeDef::ModuleDef(it) => ctx.is_deprecated_assoc_item(it), | |
402 | ScopeDef::GenericParam(it) => ctx.is_deprecated(it), | |
403 | ScopeDef::AdtSelfType(it) => ctx.is_deprecated(it), | |
404 | _ => false, | |
405 | } | |
406 | } | |
407 | ||
408 | fn compute_type_match( | |
409 | ctx: &CompletionContext<'_>, | |
410 | completion_ty: &hir::Type, | |
411 | ) -> Option<CompletionRelevanceTypeMatch> { | |
412 | let expected_type = ctx.expected_type.as_ref()?; | |
413 | ||
414 | // We don't ever consider unit type to be an exact type match, since | |
415 | // nearly always this is not meaningful to the user. | |
416 | if expected_type.is_unit() { | |
417 | return None; | |
418 | } | |
419 | ||
420 | if completion_ty == expected_type { | |
421 | Some(CompletionRelevanceTypeMatch::Exact) | |
422 | } else if expected_type.could_unify_with(ctx.db, completion_ty) { | |
423 | Some(CompletionRelevanceTypeMatch::CouldUnify) | |
424 | } else { | |
425 | None | |
426 | } | |
427 | } | |
428 | ||
429 | fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool { | |
430 | ctx.expected_name.as_ref().map_or(false, |name| name.text() == completion_name) | |
431 | } | |
432 | ||
433 | fn compute_ref_match( | |
434 | ctx: &CompletionContext<'_>, | |
435 | completion_ty: &hir::Type, | |
436 | ) -> Option<hir::Mutability> { | |
437 | let expected_type = ctx.expected_type.as_ref()?; | |
438 | if completion_ty != expected_type { | |
439 | let expected_type_without_ref = expected_type.remove_ref()?; | |
440 | if completion_ty.autoderef(ctx.db).any(|deref_ty| deref_ty == expected_type_without_ref) { | |
441 | cov_mark::hit!(suggest_ref); | |
442 | let mutability = if expected_type.is_mutable_reference() { | |
443 | hir::Mutability::Mut | |
444 | } else { | |
445 | hir::Mutability::Shared | |
446 | }; | |
447 | return Some(mutability); | |
448 | }; | |
449 | } | |
450 | None | |
451 | } | |
452 | ||
f2b60f7d FG |
453 | fn path_ref_match( |
454 | completion: &CompletionContext<'_>, | |
455 | path_ctx: &PathCompletionCtx, | |
456 | ty: &hir::Type, | |
457 | item: &mut Builder, | |
458 | ) { | |
459 | if let Some(original_path) = &path_ctx.original_path { | |
460 | // At least one char was typed by the user already, in that case look for the original path | |
461 | if let Some(original_path) = completion.sema.original_ast_node(original_path.clone()) { | |
462 | if let Some(ref_match) = compute_ref_match(completion, ty) { | |
463 | item.ref_match(ref_match, original_path.syntax().text_range().start()); | |
464 | } | |
465 | } | |
466 | } else { | |
467 | // completion requested on an empty identifier, there is no path here yet. | |
468 | // FIXME: This might create inconsistent completions where we show a ref match in macro inputs | |
469 | // as long as nothing was typed yet | |
470 | if let Some(ref_match) = compute_ref_match(completion, ty) { | |
471 | item.ref_match(ref_match, completion.position.offset); | |
472 | } | |
473 | } | |
474 | } | |
475 | ||
064997fb FG |
476 | #[cfg(test)] |
477 | mod tests { | |
478 | use std::cmp; | |
479 | ||
480 | use expect_test::{expect, Expect}; | |
481 | use ide_db::SymbolKind; | |
482 | use itertools::Itertools; | |
483 | ||
484 | use crate::{ | |
485 | item::CompletionRelevanceTypeMatch, | |
486 | tests::{check_edit, do_completion, get_all_items, TEST_CONFIG}, | |
487 | CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch, | |
488 | }; | |
489 | ||
490 | #[track_caller] | |
491 | fn check(ra_fixture: &str, kind: impl Into<CompletionItemKind>, expect: Expect) { | |
492 | let actual = do_completion(ra_fixture, kind.into()); | |
493 | expect.assert_debug_eq(&actual); | |
494 | } | |
495 | ||
496 | #[track_caller] | |
497 | fn check_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) { | |
498 | let actual: Vec<_> = | |
499 | kinds.iter().flat_map(|&kind| do_completion(ra_fixture, kind)).collect(); | |
500 | expect.assert_debug_eq(&actual); | |
501 | } | |
502 | ||
503 | #[track_caller] | |
504 | fn check_relevance_for_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) { | |
505 | let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None); | |
506 | actual.retain(|it| kinds.contains(&it.kind())); | |
507 | actual.sort_by_key(|it| cmp::Reverse(it.relevance().score())); | |
508 | check_relevance_(actual, expect); | |
509 | } | |
510 | ||
511 | #[track_caller] | |
512 | fn check_relevance(ra_fixture: &str, expect: Expect) { | |
513 | let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None); | |
514 | actual.retain(|it| it.kind() != CompletionItemKind::Snippet); | |
515 | actual.retain(|it| it.kind() != CompletionItemKind::Keyword); | |
516 | actual.retain(|it| it.kind() != CompletionItemKind::BuiltinType); | |
517 | actual.sort_by_key(|it| cmp::Reverse(it.relevance().score())); | |
518 | check_relevance_(actual, expect); | |
519 | } | |
520 | ||
521 | #[track_caller] | |
522 | fn check_relevance_(actual: Vec<CompletionItem>, expect: Expect) { | |
523 | let actual = actual | |
524 | .into_iter() | |
525 | .flat_map(|it| { | |
526 | let mut items = vec![]; | |
527 | ||
528 | let tag = it.kind().tag(); | |
529 | let relevance = display_relevance(it.relevance()); | |
9c376795 | 530 | items.push(format!("{tag} {} {relevance}\n", it.label())); |
064997fb FG |
531 | |
532 | if let Some((mutability, _offset, relevance)) = it.ref_match() { | |
533 | let label = format!("&{}{}", mutability.as_keyword_for_ref(), it.label()); | |
534 | let relevance = display_relevance(relevance); | |
535 | ||
9c376795 | 536 | items.push(format!("{tag} {label} {relevance}\n")); |
064997fb FG |
537 | } |
538 | ||
539 | items | |
540 | }) | |
541 | .collect::<String>(); | |
542 | ||
543 | expect.assert_eq(&actual); | |
544 | ||
545 | fn display_relevance(relevance: CompletionRelevance) -> String { | |
546 | let relevance_factors = vec![ | |
547 | (relevance.type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"), | |
548 | ( | |
549 | relevance.type_match == Some(CompletionRelevanceTypeMatch::CouldUnify), | |
550 | "type_could_unify", | |
551 | ), | |
552 | (relevance.exact_name_match, "name"), | |
553 | (relevance.is_local, "local"), | |
554 | ( | |
555 | relevance.postfix_match == Some(CompletionRelevancePostfixMatch::Exact), | |
556 | "snippet", | |
557 | ), | |
558 | (relevance.is_op_method, "op_method"), | |
559 | (relevance.requires_import, "requires_import"), | |
560 | ] | |
561 | .into_iter() | |
562 | .filter_map(|(cond, desc)| if cond { Some(desc) } else { None }) | |
563 | .join("+"); | |
564 | ||
9c376795 | 565 | format!("[{relevance_factors}]") |
064997fb FG |
566 | } |
567 | } | |
568 | ||
569 | #[test] | |
570 | fn enum_detail_includes_record_fields() { | |
571 | check( | |
572 | r#" | |
573 | enum Foo { Foo { x: i32, y: i32 } } | |
574 | ||
575 | fn main() { Foo::Fo$0 } | |
576 | "#, | |
577 | SymbolKind::Variant, | |
578 | expect![[r#" | |
579 | [ | |
580 | CompletionItem { | |
581 | label: "Foo {…}", | |
582 | source_range: 54..56, | |
583 | delete: 54..56, | |
584 | insert: "Foo { x: ${1:()}, y: ${2:()} }$0", | |
585 | kind: SymbolKind( | |
586 | Variant, | |
587 | ), | |
f2b60f7d | 588 | lookup: "Foo{}", |
064997fb FG |
589 | detail: "Foo { x: i32, y: i32 }", |
590 | }, | |
591 | ] | |
592 | "#]], | |
593 | ); | |
594 | } | |
595 | ||
596 | #[test] | |
597 | fn enum_detail_includes_tuple_fields() { | |
598 | check( | |
599 | r#" | |
600 | enum Foo { Foo (i32, i32) } | |
601 | ||
602 | fn main() { Foo::Fo$0 } | |
603 | "#, | |
604 | SymbolKind::Variant, | |
605 | expect![[r#" | |
606 | [ | |
607 | CompletionItem { | |
608 | label: "Foo(…)", | |
609 | source_range: 46..48, | |
610 | delete: 46..48, | |
611 | insert: "Foo(${1:()}, ${2:()})$0", | |
612 | kind: SymbolKind( | |
613 | Variant, | |
614 | ), | |
f2b60f7d | 615 | lookup: "Foo()", |
064997fb FG |
616 | detail: "Foo(i32, i32)", |
617 | }, | |
618 | ] | |
619 | "#]], | |
620 | ); | |
621 | } | |
622 | ||
623 | #[test] | |
624 | fn fn_detail_includes_args_and_return_type() { | |
625 | check( | |
626 | r#" | |
627 | fn foo<T>(a: u32, b: u32, t: T) -> (u32, T) { (a, t) } | |
628 | ||
629 | fn main() { fo$0 } | |
630 | "#, | |
631 | SymbolKind::Function, | |
632 | expect![[r#" | |
633 | [ | |
634 | CompletionItem { | |
635 | label: "foo(…)", | |
636 | source_range: 68..70, | |
637 | delete: 68..70, | |
638 | insert: "foo(${1:a}, ${2:b}, ${3:t})$0", | |
639 | kind: SymbolKind( | |
640 | Function, | |
641 | ), | |
642 | lookup: "foo", | |
643 | detail: "fn(u32, u32, T) -> (u32, T)", | |
644 | trigger_call_info: true, | |
645 | }, | |
646 | CompletionItem { | |
647 | label: "main()", | |
648 | source_range: 68..70, | |
649 | delete: 68..70, | |
650 | insert: "main()$0", | |
651 | kind: SymbolKind( | |
652 | Function, | |
653 | ), | |
654 | lookup: "main", | |
655 | detail: "fn()", | |
656 | }, | |
657 | ] | |
658 | "#]], | |
659 | ); | |
660 | } | |
661 | ||
662 | #[test] | |
663 | fn enum_detail_just_name_for_unit() { | |
664 | check( | |
665 | r#" | |
666 | enum Foo { Foo } | |
667 | ||
668 | fn main() { Foo::Fo$0 } | |
669 | "#, | |
670 | SymbolKind::Variant, | |
671 | expect![[r#" | |
672 | [ | |
673 | CompletionItem { | |
674 | label: "Foo", | |
675 | source_range: 35..37, | |
676 | delete: 35..37, | |
677 | insert: "Foo$0", | |
678 | kind: SymbolKind( | |
679 | Variant, | |
680 | ), | |
681 | detail: "Foo", | |
682 | }, | |
683 | ] | |
684 | "#]], | |
685 | ); | |
686 | } | |
687 | ||
688 | #[test] | |
689 | fn lookup_enums_by_two_qualifiers() { | |
690 | check_kinds( | |
691 | r#" | |
692 | mod m { | |
693 | pub enum Spam { Foo, Bar(i32) } | |
694 | } | |
695 | fn main() { let _: m::Spam = S$0 } | |
696 | "#, | |
697 | &[ | |
698 | CompletionItemKind::SymbolKind(SymbolKind::Function), | |
699 | CompletionItemKind::SymbolKind(SymbolKind::Module), | |
700 | CompletionItemKind::SymbolKind(SymbolKind::Variant), | |
701 | ], | |
702 | expect![[r#" | |
703 | [ | |
704 | CompletionItem { | |
705 | label: "main()", | |
706 | source_range: 75..76, | |
707 | delete: 75..76, | |
708 | insert: "main()$0", | |
709 | kind: SymbolKind( | |
710 | Function, | |
711 | ), | |
712 | lookup: "main", | |
713 | detail: "fn()", | |
714 | }, | |
715 | CompletionItem { | |
716 | label: "m", | |
717 | source_range: 75..76, | |
718 | delete: 75..76, | |
719 | insert: "m", | |
720 | kind: SymbolKind( | |
721 | Module, | |
722 | ), | |
723 | }, | |
724 | CompletionItem { | |
725 | label: "m::Spam::Bar(…)", | |
726 | source_range: 75..76, | |
727 | delete: 75..76, | |
728 | insert: "m::Spam::Bar(${1:()})$0", | |
729 | kind: SymbolKind( | |
730 | Variant, | |
731 | ), | |
f2b60f7d | 732 | lookup: "Spam::Bar()", |
064997fb FG |
733 | detail: "m::Spam::Bar(i32)", |
734 | relevance: CompletionRelevance { | |
735 | exact_name_match: false, | |
736 | type_match: Some( | |
737 | Exact, | |
738 | ), | |
739 | is_local: false, | |
740 | is_item_from_trait: false, | |
741 | is_name_already_imported: false, | |
742 | requires_import: false, | |
743 | is_op_method: false, | |
744 | is_private_editable: false, | |
745 | postfix_match: None, | |
746 | is_definite: false, | |
747 | }, | |
748 | }, | |
749 | CompletionItem { | |
750 | label: "m::Spam::Foo", | |
751 | source_range: 75..76, | |
752 | delete: 75..76, | |
753 | insert: "m::Spam::Foo$0", | |
754 | kind: SymbolKind( | |
755 | Variant, | |
756 | ), | |
757 | lookup: "Spam::Foo", | |
758 | detail: "m::Spam::Foo", | |
759 | relevance: CompletionRelevance { | |
760 | exact_name_match: false, | |
761 | type_match: Some( | |
762 | Exact, | |
763 | ), | |
764 | is_local: false, | |
765 | is_item_from_trait: false, | |
766 | is_name_already_imported: false, | |
767 | requires_import: false, | |
768 | is_op_method: false, | |
769 | is_private_editable: false, | |
770 | postfix_match: None, | |
771 | is_definite: false, | |
772 | }, | |
773 | }, | |
774 | ] | |
775 | "#]], | |
776 | ) | |
777 | } | |
778 | ||
779 | #[test] | |
780 | fn sets_deprecated_flag_in_items() { | |
781 | check( | |
782 | r#" | |
783 | #[deprecated] | |
784 | fn something_deprecated() {} | |
785 | ||
786 | fn main() { som$0 } | |
787 | "#, | |
788 | SymbolKind::Function, | |
789 | expect![[r#" | |
790 | [ | |
791 | CompletionItem { | |
792 | label: "main()", | |
793 | source_range: 56..59, | |
794 | delete: 56..59, | |
795 | insert: "main()$0", | |
796 | kind: SymbolKind( | |
797 | Function, | |
798 | ), | |
799 | lookup: "main", | |
800 | detail: "fn()", | |
801 | }, | |
802 | CompletionItem { | |
803 | label: "something_deprecated()", | |
804 | source_range: 56..59, | |
805 | delete: 56..59, | |
806 | insert: "something_deprecated()$0", | |
807 | kind: SymbolKind( | |
808 | Function, | |
809 | ), | |
810 | lookup: "something_deprecated", | |
811 | detail: "fn()", | |
812 | deprecated: true, | |
813 | }, | |
814 | ] | |
815 | "#]], | |
816 | ); | |
817 | ||
818 | check( | |
819 | r#" | |
820 | struct A { #[deprecated] the_field: u32 } | |
821 | fn foo() { A { the$0 } } | |
822 | "#, | |
823 | SymbolKind::Field, | |
824 | expect![[r#" | |
825 | [ | |
826 | CompletionItem { | |
827 | label: "the_field", | |
828 | source_range: 57..60, | |
829 | delete: 57..60, | |
830 | insert: "the_field", | |
831 | kind: SymbolKind( | |
832 | Field, | |
833 | ), | |
834 | detail: "u32", | |
835 | deprecated: true, | |
836 | relevance: CompletionRelevance { | |
837 | exact_name_match: false, | |
838 | type_match: Some( | |
839 | CouldUnify, | |
840 | ), | |
841 | is_local: false, | |
842 | is_item_from_trait: false, | |
843 | is_name_already_imported: false, | |
844 | requires_import: false, | |
845 | is_op_method: false, | |
846 | is_private_editable: false, | |
847 | postfix_match: None, | |
848 | is_definite: false, | |
849 | }, | |
850 | }, | |
851 | ] | |
852 | "#]], | |
853 | ); | |
854 | } | |
855 | ||
856 | #[test] | |
857 | fn renders_docs() { | |
858 | check_kinds( | |
859 | r#" | |
860 | struct S { | |
861 | /// Field docs | |
862 | foo: | |
863 | } | |
864 | impl S { | |
865 | /// Method docs | |
866 | fn bar(self) { self.$0 } | |
867 | }"#, | |
868 | &[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)], | |
869 | expect![[r#" | |
870 | [ | |
871 | CompletionItem { | |
872 | label: "bar()", | |
873 | source_range: 94..94, | |
874 | delete: 94..94, | |
875 | insert: "bar()$0", | |
876 | kind: Method, | |
877 | lookup: "bar", | |
878 | detail: "fn(self)", | |
879 | documentation: Documentation( | |
880 | "Method docs", | |
881 | ), | |
882 | }, | |
883 | CompletionItem { | |
884 | label: "foo", | |
885 | source_range: 94..94, | |
886 | delete: 94..94, | |
887 | insert: "foo", | |
888 | kind: SymbolKind( | |
889 | Field, | |
890 | ), | |
891 | detail: "{unknown}", | |
892 | documentation: Documentation( | |
893 | "Field docs", | |
894 | ), | |
895 | }, | |
896 | ] | |
897 | "#]], | |
898 | ); | |
899 | ||
900 | check_kinds( | |
901 | r#" | |
902 | use self::my$0; | |
903 | ||
904 | /// mod docs | |
905 | mod my { } | |
906 | ||
907 | /// enum docs | |
908 | enum E { | |
909 | /// variant docs | |
910 | V | |
911 | } | |
912 | use self::E::*; | |
913 | "#, | |
914 | &[ | |
915 | CompletionItemKind::SymbolKind(SymbolKind::Module), | |
916 | CompletionItemKind::SymbolKind(SymbolKind::Variant), | |
917 | CompletionItemKind::SymbolKind(SymbolKind::Enum), | |
918 | ], | |
919 | expect![[r#" | |
920 | [ | |
921 | CompletionItem { | |
922 | label: "my", | |
923 | source_range: 10..12, | |
924 | delete: 10..12, | |
925 | insert: "my", | |
926 | kind: SymbolKind( | |
927 | Module, | |
928 | ), | |
929 | documentation: Documentation( | |
930 | "mod docs", | |
931 | ), | |
932 | }, | |
933 | CompletionItem { | |
934 | label: "V", | |
935 | source_range: 10..12, | |
936 | delete: 10..12, | |
937 | insert: "V$0", | |
938 | kind: SymbolKind( | |
939 | Variant, | |
940 | ), | |
941 | detail: "V", | |
942 | documentation: Documentation( | |
943 | "variant docs", | |
944 | ), | |
945 | }, | |
946 | CompletionItem { | |
947 | label: "E", | |
948 | source_range: 10..12, | |
949 | delete: 10..12, | |
950 | insert: "E", | |
951 | kind: SymbolKind( | |
952 | Enum, | |
953 | ), | |
954 | documentation: Documentation( | |
955 | "enum docs", | |
956 | ), | |
957 | }, | |
958 | ] | |
959 | "#]], | |
960 | ) | |
961 | } | |
962 | ||
963 | #[test] | |
964 | fn dont_render_attrs() { | |
965 | check( | |
966 | r#" | |
967 | struct S; | |
968 | impl S { | |
969 | #[inline] | |
970 | fn the_method(&self) { } | |
971 | } | |
972 | fn foo(s: S) { s.$0 } | |
973 | "#, | |
974 | CompletionItemKind::Method, | |
975 | expect![[r#" | |
976 | [ | |
977 | CompletionItem { | |
978 | label: "the_method()", | |
979 | source_range: 81..81, | |
980 | delete: 81..81, | |
981 | insert: "the_method()$0", | |
982 | kind: Method, | |
983 | lookup: "the_method", | |
984 | detail: "fn(&self)", | |
985 | }, | |
986 | ] | |
987 | "#]], | |
988 | ) | |
989 | } | |
990 | ||
991 | #[test] | |
992 | fn no_call_parens_if_fn_ptr_needed() { | |
993 | cov_mark::check!(no_call_parens_if_fn_ptr_needed); | |
994 | check_edit( | |
995 | "foo", | |
996 | r#" | |
997 | fn foo(foo: u8, bar: u8) {} | |
998 | struct ManualVtable { f: fn(u8, u8) } | |
999 | ||
1000 | fn main() -> ManualVtable { | |
1001 | ManualVtable { f: f$0 } | |
1002 | } | |
1003 | "#, | |
1004 | r#" | |
1005 | fn foo(foo: u8, bar: u8) {} | |
1006 | struct ManualVtable { f: fn(u8, u8) } | |
1007 | ||
1008 | fn main() -> ManualVtable { | |
1009 | ManualVtable { f: foo } | |
1010 | } | |
1011 | "#, | |
1012 | ); | |
1013 | check_edit( | |
1014 | "type", | |
1015 | r#" | |
1016 | struct RawIdentTable { r#type: u32 } | |
1017 | ||
1018 | fn main() -> RawIdentTable { | |
1019 | RawIdentTable { t$0: 42 } | |
1020 | } | |
1021 | "#, | |
1022 | r#" | |
1023 | struct RawIdentTable { r#type: u32 } | |
1024 | ||
1025 | fn main() -> RawIdentTable { | |
1026 | RawIdentTable { r#type: 42 } | |
1027 | } | |
1028 | "#, | |
1029 | ); | |
1030 | } | |
1031 | ||
1032 | #[test] | |
1033 | fn no_parens_in_use_item() { | |
1034 | check_edit( | |
1035 | "foo", | |
1036 | r#" | |
1037 | mod m { pub fn foo() {} } | |
1038 | use crate::m::f$0; | |
1039 | "#, | |
1040 | r#" | |
1041 | mod m { pub fn foo() {} } | |
1042 | use crate::m::foo; | |
1043 | "#, | |
1044 | ); | |
1045 | } | |
1046 | ||
1047 | #[test] | |
1048 | fn no_parens_in_call() { | |
1049 | check_edit( | |
1050 | "foo", | |
1051 | r#" | |
1052 | fn foo(x: i32) {} | |
1053 | fn main() { f$0(); } | |
1054 | "#, | |
1055 | r#" | |
1056 | fn foo(x: i32) {} | |
1057 | fn main() { foo(); } | |
1058 | "#, | |
1059 | ); | |
1060 | check_edit( | |
1061 | "foo", | |
1062 | r#" | |
1063 | struct Foo; | |
1064 | impl Foo { fn foo(&self){} } | |
1065 | fn f(foo: &Foo) { foo.f$0(); } | |
1066 | "#, | |
1067 | r#" | |
1068 | struct Foo; | |
1069 | impl Foo { fn foo(&self){} } | |
1070 | fn f(foo: &Foo) { foo.foo(); } | |
1071 | "#, | |
1072 | ); | |
1073 | } | |
1074 | ||
1075 | #[test] | |
1076 | fn inserts_angle_brackets_for_generics() { | |
1077 | cov_mark::check!(inserts_angle_brackets_for_generics); | |
1078 | check_edit( | |
1079 | "Vec", | |
1080 | r#" | |
1081 | struct Vec<T> {} | |
1082 | fn foo(xs: Ve$0) | |
1083 | "#, | |
1084 | r#" | |
1085 | struct Vec<T> {} | |
1086 | fn foo(xs: Vec<$0>) | |
1087 | "#, | |
1088 | ); | |
1089 | check_edit( | |
1090 | "Vec", | |
1091 | r#" | |
1092 | type Vec<T> = (T,); | |
1093 | fn foo(xs: Ve$0) | |
1094 | "#, | |
1095 | r#" | |
1096 | type Vec<T> = (T,); | |
1097 | fn foo(xs: Vec<$0>) | |
1098 | "#, | |
1099 | ); | |
1100 | check_edit( | |
1101 | "Vec", | |
1102 | r#" | |
1103 | struct Vec<T = i128> {} | |
1104 | fn foo(xs: Ve$0) | |
1105 | "#, | |
1106 | r#" | |
1107 | struct Vec<T = i128> {} | |
1108 | fn foo(xs: Vec) | |
1109 | "#, | |
1110 | ); | |
1111 | check_edit( | |
1112 | "Vec", | |
1113 | r#" | |
1114 | struct Vec<T> {} | |
1115 | fn foo(xs: Ve$0<i128>) | |
1116 | "#, | |
1117 | r#" | |
1118 | struct Vec<T> {} | |
1119 | fn foo(xs: Vec<i128>) | |
1120 | "#, | |
1121 | ); | |
1122 | } | |
1123 | ||
1124 | #[test] | |
1125 | fn active_param_relevance() { | |
1126 | check_relevance( | |
1127 | r#" | |
1128 | struct S { foo: i64, bar: u32, baz: u32 } | |
1129 | fn test(bar: u32) { } | |
1130 | fn foo(s: S) { test(s.$0) } | |
1131 | "#, | |
1132 | expect![[r#" | |
1133 | fd bar [type+name] | |
1134 | fd baz [type] | |
1135 | fd foo [] | |
1136 | "#]], | |
1137 | ); | |
1138 | } | |
1139 | ||
1140 | #[test] | |
1141 | fn record_field_relevances() { | |
1142 | check_relevance( | |
1143 | r#" | |
1144 | struct A { foo: i64, bar: u32, baz: u32 } | |
1145 | struct B { x: (), y: f32, bar: u32 } | |
1146 | fn foo(a: A) { B { bar: a.$0 }; } | |
1147 | "#, | |
1148 | expect![[r#" | |
1149 | fd bar [type+name] | |
1150 | fd baz [type] | |
1151 | fd foo [] | |
1152 | "#]], | |
1153 | ) | |
1154 | } | |
1155 | ||
1156 | #[test] | |
1157 | fn record_field_and_call_relevances() { | |
1158 | check_relevance( | |
1159 | r#" | |
1160 | struct A { foo: i64, bar: u32, baz: u32 } | |
1161 | struct B { x: (), y: f32, bar: u32 } | |
1162 | fn f(foo: i64) { } | |
1163 | fn foo(a: A) { B { bar: f(a.$0) }; } | |
1164 | "#, | |
1165 | expect![[r#" | |
1166 | fd foo [type+name] | |
1167 | fd bar [] | |
1168 | fd baz [] | |
1169 | "#]], | |
1170 | ); | |
1171 | check_relevance( | |
1172 | r#" | |
1173 | struct A { foo: i64, bar: u32, baz: u32 } | |
1174 | struct B { x: (), y: f32, bar: u32 } | |
1175 | fn f(foo: i64) { } | |
1176 | fn foo(a: A) { f(B { bar: a.$0 }); } | |
1177 | "#, | |
1178 | expect![[r#" | |
1179 | fd bar [type+name] | |
1180 | fd baz [type] | |
1181 | fd foo [] | |
1182 | "#]], | |
1183 | ); | |
1184 | } | |
1185 | ||
1186 | #[test] | |
1187 | fn prioritize_exact_ref_match() { | |
1188 | check_relevance( | |
1189 | r#" | |
1190 | struct WorldSnapshot { _f: () }; | |
1191 | fn go(world: &WorldSnapshot) { go(w$0) } | |
1192 | "#, | |
1193 | expect![[r#" | |
1194 | lc world [type+name+local] | |
1195 | st WorldSnapshot {…} [] | |
1196 | st &WorldSnapshot {…} [type] | |
1197 | st WorldSnapshot [] | |
1198 | fn go(…) [] | |
1199 | "#]], | |
1200 | ); | |
1201 | } | |
1202 | ||
1203 | #[test] | |
1204 | fn too_many_arguments() { | |
1205 | cov_mark::check!(too_many_arguments); | |
1206 | check_relevance( | |
1207 | r#" | |
1208 | struct Foo; | |
1209 | fn f(foo: &Foo) { f(foo, w$0) } | |
1210 | "#, | |
1211 | expect![[r#" | |
1212 | lc foo [local] | |
1213 | st Foo [] | |
1214 | fn f(…) [] | |
1215 | "#]], | |
1216 | ); | |
1217 | } | |
1218 | ||
1219 | #[test] | |
1220 | fn score_fn_type_and_name_match() { | |
1221 | check_relevance( | |
1222 | r#" | |
1223 | struct A { bar: u8 } | |
1224 | fn baz() -> u8 { 0 } | |
1225 | fn bar() -> u8 { 0 } | |
1226 | fn f() { A { bar: b$0 }; } | |
1227 | "#, | |
1228 | expect![[r#" | |
1229 | fn bar() [type+name] | |
1230 | fn baz() [type] | |
1231 | st A [] | |
1232 | fn f() [] | |
1233 | "#]], | |
1234 | ); | |
1235 | } | |
1236 | ||
1237 | #[test] | |
1238 | fn score_method_type_and_name_match() { | |
1239 | check_relevance( | |
1240 | r#" | |
1241 | fn baz(aaa: u32){} | |
1242 | struct Foo; | |
1243 | impl Foo { | |
1244 | fn aaa(&self) -> u32 { 0 } | |
1245 | fn bbb(&self) -> u32 { 0 } | |
1246 | fn ccc(&self) -> u64 { 0 } | |
1247 | } | |
1248 | fn f() { | |
1249 | baz(Foo.$0 | |
1250 | } | |
1251 | "#, | |
1252 | expect![[r#" | |
1253 | me aaa() [type+name] | |
1254 | me bbb() [type] | |
1255 | me ccc() [] | |
1256 | "#]], | |
1257 | ); | |
1258 | } | |
1259 | ||
1260 | #[test] | |
1261 | fn score_method_name_match_only() { | |
1262 | check_relevance( | |
1263 | r#" | |
1264 | fn baz(aaa: u32){} | |
1265 | struct Foo; | |
1266 | impl Foo { | |
1267 | fn aaa(&self) -> u64 { 0 } | |
1268 | } | |
1269 | fn f() { | |
1270 | baz(Foo.$0 | |
1271 | } | |
1272 | "#, | |
1273 | expect![[r#" | |
1274 | me aaa() [name] | |
1275 | "#]], | |
1276 | ); | |
1277 | } | |
1278 | ||
1279 | #[test] | |
1280 | fn suggest_ref_mut() { | |
1281 | cov_mark::check!(suggest_ref); | |
1282 | check_relevance( | |
1283 | r#" | |
1284 | struct S; | |
1285 | fn foo(s: &mut S) {} | |
1286 | fn main() { | |
1287 | let mut s = S; | |
1288 | foo($0); | |
1289 | } | |
1290 | "#, | |
1291 | expect![[r#" | |
1292 | lc s [name+local] | |
1293 | lc &mut s [type+name+local] | |
1294 | st S [] | |
1295 | st &mut S [type] | |
1296 | st S [] | |
1297 | fn foo(…) [] | |
1298 | fn main() [] | |
1299 | "#]], | |
1300 | ); | |
1301 | check_relevance( | |
1302 | r#" | |
1303 | struct S; | |
1304 | fn foo(s: &mut S) {} | |
1305 | fn main() { | |
1306 | let mut s = S; | |
1307 | foo(&mut $0); | |
1308 | } | |
1309 | "#, | |
1310 | expect![[r#" | |
1311 | lc s [type+name+local] | |
1312 | st S [type] | |
1313 | st S [] | |
1314 | fn foo(…) [] | |
1315 | fn main() [] | |
1316 | "#]], | |
1317 | ); | |
1318 | check_relevance( | |
1319 | r#" | |
1320 | struct S; | |
1321 | fn foo(s: &mut S) {} | |
1322 | fn main() { | |
1323 | let mut ssss = S; | |
1324 | foo(&mut s$0); | |
1325 | } | |
1326 | "#, | |
1327 | expect![[r#" | |
1328 | lc ssss [type+local] | |
1329 | st S [type] | |
1330 | st S [] | |
1331 | fn foo(…) [] | |
1332 | fn main() [] | |
1333 | "#]], | |
1334 | ); | |
1335 | } | |
1336 | ||
1337 | #[test] | |
1338 | fn suggest_deref() { | |
1339 | check_relevance( | |
1340 | r#" | |
1341 | //- minicore: deref | |
1342 | struct S; | |
1343 | struct T(S); | |
1344 | ||
1345 | impl core::ops::Deref for T { | |
1346 | type Target = S; | |
1347 | ||
1348 | fn deref(&self) -> &Self::Target { | |
1349 | &self.0 | |
1350 | } | |
1351 | } | |
1352 | ||
1353 | fn foo(s: &S) {} | |
1354 | ||
1355 | fn main() { | |
1356 | let t = T(S); | |
1357 | let m = 123; | |
1358 | ||
1359 | foo($0); | |
1360 | } | |
1361 | "#, | |
1362 | expect![[r#" | |
1363 | lc m [local] | |
1364 | lc t [local] | |
1365 | lc &t [type+local] | |
1366 | st S [] | |
1367 | st &S [type] | |
1368 | st S [] | |
1369 | st T [] | |
1370 | fn foo(…) [] | |
1371 | fn main() [] | |
1372 | md core [] | |
1373 | "#]], | |
1374 | ) | |
1375 | } | |
1376 | ||
1377 | #[test] | |
1378 | fn suggest_deref_mut() { | |
1379 | check_relevance( | |
1380 | r#" | |
1381 | //- minicore: deref_mut | |
1382 | struct S; | |
1383 | struct T(S); | |
1384 | ||
1385 | impl core::ops::Deref for T { | |
1386 | type Target = S; | |
1387 | ||
1388 | fn deref(&self) -> &Self::Target { | |
1389 | &self.0 | |
1390 | } | |
1391 | } | |
1392 | ||
1393 | impl core::ops::DerefMut for T { | |
1394 | fn deref_mut(&mut self) -> &mut Self::Target { | |
1395 | &mut self.0 | |
1396 | } | |
1397 | } | |
1398 | ||
1399 | fn foo(s: &mut S) {} | |
1400 | ||
1401 | fn main() { | |
1402 | let t = T(S); | |
1403 | let m = 123; | |
1404 | ||
1405 | foo($0); | |
1406 | } | |
1407 | "#, | |
1408 | expect![[r#" | |
1409 | lc m [local] | |
1410 | lc t [local] | |
1411 | lc &mut t [type+local] | |
1412 | st S [] | |
1413 | st &mut S [type] | |
1414 | st S [] | |
1415 | st T [] | |
1416 | fn foo(…) [] | |
1417 | fn main() [] | |
1418 | md core [] | |
1419 | "#]], | |
1420 | ) | |
1421 | } | |
1422 | ||
1423 | #[test] | |
1424 | fn locals() { | |
1425 | check_relevance( | |
1426 | r#" | |
1427 | fn foo(bar: u32) { | |
1428 | let baz = 0; | |
1429 | ||
1430 | f$0 | |
1431 | } | |
1432 | "#, | |
1433 | expect![[r#" | |
1434 | lc baz [local] | |
1435 | lc bar [local] | |
1436 | fn foo(…) [] | |
1437 | "#]], | |
1438 | ); | |
1439 | } | |
1440 | ||
1441 | #[test] | |
1442 | fn enum_owned() { | |
1443 | check_relevance( | |
1444 | r#" | |
1445 | enum Foo { A, B } | |
1446 | fn foo() { | |
1447 | bar($0); | |
1448 | } | |
1449 | fn bar(t: Foo) {} | |
1450 | "#, | |
1451 | expect![[r#" | |
1452 | ev Foo::A [type] | |
1453 | ev Foo::B [type] | |
1454 | en Foo [] | |
1455 | fn bar(…) [] | |
1456 | fn foo() [] | |
1457 | "#]], | |
1458 | ); | |
1459 | } | |
1460 | ||
1461 | #[test] | |
1462 | fn enum_ref() { | |
1463 | check_relevance( | |
1464 | r#" | |
1465 | enum Foo { A, B } | |
1466 | fn foo() { | |
1467 | bar($0); | |
1468 | } | |
1469 | fn bar(t: &Foo) {} | |
1470 | "#, | |
1471 | expect![[r#" | |
1472 | ev Foo::A [] | |
1473 | ev &Foo::A [type] | |
1474 | ev Foo::B [] | |
1475 | ev &Foo::B [type] | |
1476 | en Foo [] | |
1477 | fn bar(…) [] | |
1478 | fn foo() [] | |
1479 | "#]], | |
1480 | ); | |
1481 | } | |
1482 | ||
1483 | #[test] | |
1484 | fn suggest_deref_fn_ret() { | |
1485 | check_relevance( | |
1486 | r#" | |
1487 | //- minicore: deref | |
1488 | struct S; | |
1489 | struct T(S); | |
1490 | ||
1491 | impl core::ops::Deref for T { | |
1492 | type Target = S; | |
1493 | ||
1494 | fn deref(&self) -> &Self::Target { | |
1495 | &self.0 | |
1496 | } | |
1497 | } | |
1498 | ||
1499 | fn foo(s: &S) {} | |
1500 | fn bar() -> T {} | |
1501 | ||
1502 | fn main() { | |
1503 | foo($0); | |
1504 | } | |
1505 | "#, | |
1506 | expect![[r#" | |
1507 | st S [] | |
1508 | st &S [type] | |
1509 | st S [] | |
1510 | st T [] | |
1511 | fn bar() [] | |
1512 | fn &bar() [type] | |
1513 | fn foo(…) [] | |
1514 | fn main() [] | |
1515 | md core [] | |
1516 | "#]], | |
1517 | ) | |
1518 | } | |
1519 | ||
1520 | #[test] | |
1521 | fn op_function_relevances() { | |
1522 | check_relevance( | |
1523 | r#" | |
1524 | #[lang = "sub"] | |
1525 | trait Sub { | |
1526 | fn sub(self, other: Self) -> Self { self } | |
1527 | } | |
1528 | impl Sub for u32 {} | |
1529 | fn foo(a: u32) { a.$0 } | |
1530 | "#, | |
1531 | expect![[r#" | |
1532 | me sub(…) (as Sub) [op_method] | |
1533 | "#]], | |
1534 | ); | |
1535 | check_relevance( | |
1536 | r#" | |
1537 | struct Foo; | |
1538 | impl Foo { | |
1539 | fn new() -> Self {} | |
1540 | } | |
1541 | #[lang = "eq"] | |
1542 | pub trait PartialEq<Rhs: ?Sized = Self> { | |
1543 | fn eq(&self, other: &Rhs) -> bool; | |
1544 | fn ne(&self, other: &Rhs) -> bool; | |
1545 | } | |
1546 | ||
1547 | impl PartialEq for Foo {} | |
1548 | fn main() { | |
1549 | Foo::$0 | |
1550 | } | |
1551 | "#, | |
1552 | expect![[r#" | |
1553 | fn new() [] | |
1554 | me eq(…) (as PartialEq) [op_method] | |
1555 | me ne(…) (as PartialEq) [op_method] | |
1556 | "#]], | |
1557 | ); | |
1558 | } | |
1559 | ||
1560 | #[test] | |
1561 | fn struct_field_method_ref() { | |
1562 | check_kinds( | |
1563 | r#" | |
1564 | struct Foo { bar: u32 } | |
1565 | impl Foo { fn baz(&self) -> u32 { 0 } } | |
1566 | ||
1567 | fn foo(f: Foo) { let _: &u32 = f.b$0 } | |
1568 | "#, | |
1569 | &[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)], | |
1570 | expect![[r#" | |
1571 | [ | |
1572 | CompletionItem { | |
1573 | label: "baz()", | |
1574 | source_range: 98..99, | |
1575 | delete: 98..99, | |
1576 | insert: "baz()$0", | |
1577 | kind: Method, | |
1578 | lookup: "baz", | |
1579 | detail: "fn(&self) -> u32", | |
1580 | ref_match: "&@96", | |
1581 | }, | |
1582 | CompletionItem { | |
1583 | label: "bar", | |
1584 | source_range: 98..99, | |
1585 | delete: 98..99, | |
1586 | insert: "bar", | |
1587 | kind: SymbolKind( | |
1588 | Field, | |
1589 | ), | |
1590 | detail: "u32", | |
1591 | ref_match: "&@96", | |
1592 | }, | |
1593 | ] | |
1594 | "#]], | |
1595 | ); | |
1596 | } | |
1597 | ||
1598 | #[test] | |
1599 | fn qualified_path_ref() { | |
1600 | check_kinds( | |
1601 | r#" | |
1602 | struct S; | |
1603 | ||
1604 | struct T; | |
1605 | impl T { | |
1606 | fn foo() -> S {} | |
1607 | } | |
1608 | ||
1609 | fn bar(s: &S) {} | |
1610 | ||
1611 | fn main() { | |
1612 | bar(T::$0); | |
1613 | } | |
1614 | "#, | |
1615 | &[CompletionItemKind::SymbolKind(SymbolKind::Function)], | |
1616 | expect![[r#" | |
1617 | [ | |
1618 | CompletionItem { | |
1619 | label: "foo()", | |
1620 | source_range: 95..95, | |
1621 | delete: 95..95, | |
1622 | insert: "foo()$0", | |
1623 | kind: SymbolKind( | |
1624 | Function, | |
1625 | ), | |
1626 | lookup: "foo", | |
1627 | detail: "fn() -> S", | |
1628 | ref_match: "&@92", | |
1629 | }, | |
1630 | ] | |
1631 | "#]], | |
1632 | ); | |
1633 | } | |
1634 | ||
1635 | #[test] | |
1636 | fn generic_enum() { | |
1637 | check_relevance( | |
1638 | r#" | |
1639 | enum Foo<T> { A(T), B } | |
1640 | // bar() should not be an exact type match | |
1641 | // because the generic parameters are different | |
1642 | fn bar() -> Foo<u8> { Foo::B } | |
1643 | // FIXME baz() should be an exact type match | |
1644 | // because the types could unify, but it currently | |
1645 | // is not. This is due to the T here being | |
1646 | // TyKind::Placeholder rather than TyKind::Missing. | |
1647 | fn baz<T>() -> Foo<T> { Foo::B } | |
1648 | fn foo() { | |
1649 | let foo: Foo<u32> = Foo::B; | |
1650 | let _: Foo<u32> = f$0; | |
1651 | } | |
1652 | "#, | |
1653 | expect![[r#" | |
1654 | lc foo [type+local] | |
1655 | ev Foo::A(…) [type_could_unify] | |
1656 | ev Foo::B [type_could_unify] | |
1657 | fn foo() [] | |
1658 | en Foo [] | |
1659 | fn bar() [] | |
1660 | fn baz() [] | |
1661 | "#]], | |
1662 | ); | |
1663 | } | |
1664 | ||
1665 | #[test] | |
1666 | fn postfix_exact_match_is_high_priority() { | |
1667 | cov_mark::check!(postfix_exact_match_is_high_priority); | |
1668 | check_relevance_for_kinds( | |
1669 | r#" | |
1670 | mod ops { | |
1671 | pub trait Not { | |
1672 | type Output; | |
1673 | fn not(self) -> Self::Output; | |
1674 | } | |
1675 | ||
1676 | impl Not for bool { | |
1677 | type Output = bool; | |
1678 | fn not(self) -> bool { if self { false } else { true }} | |
1679 | } | |
1680 | } | |
1681 | ||
1682 | fn main() { | |
1683 | let _: bool = (9 > 2).not$0; | |
1684 | } | |
1685 | "#, | |
1686 | &[CompletionItemKind::Snippet, CompletionItemKind::Method], | |
1687 | expect![[r#" | |
1688 | sn not [snippet] | |
1689 | me not() (use ops::Not) [type_could_unify+requires_import] | |
1690 | sn if [] | |
1691 | sn while [] | |
1692 | sn ref [] | |
1693 | sn refm [] | |
1694 | sn match [] | |
1695 | sn box [] | |
1696 | sn dbg [] | |
1697 | sn dbgr [] | |
1698 | sn call [] | |
1699 | "#]], | |
1700 | ); | |
1701 | } | |
1702 | ||
1703 | #[test] | |
1704 | fn postfix_inexact_match_is_low_priority() { | |
1705 | cov_mark::check!(postfix_inexact_match_is_low_priority); | |
1706 | check_relevance_for_kinds( | |
1707 | r#" | |
1708 | struct S; | |
1709 | impl S { | |
1710 | fn f(&self) {} | |
1711 | } | |
1712 | fn main() { | |
1713 | S.$0 | |
1714 | } | |
1715 | "#, | |
1716 | &[CompletionItemKind::Snippet, CompletionItemKind::Method], | |
1717 | expect![[r#" | |
1718 | me f() [] | |
1719 | sn ref [] | |
1720 | sn refm [] | |
1721 | sn match [] | |
1722 | sn box [] | |
1723 | sn dbg [] | |
1724 | sn dbgr [] | |
1725 | sn call [] | |
1726 | sn let [] | |
1727 | sn letm [] | |
1728 | "#]], | |
1729 | ); | |
1730 | } | |
1731 | ||
1732 | #[test] | |
1733 | fn flyimport_reduced_relevance() { | |
1734 | check_relevance( | |
1735 | r#" | |
1736 | mod std { | |
1737 | pub mod io { | |
1738 | pub trait BufRead {} | |
1739 | pub struct BufReader; | |
1740 | pub struct BufWriter; | |
1741 | } | |
1742 | } | |
1743 | struct Buffer; | |
1744 | ||
1745 | fn f() { | |
1746 | Buf$0 | |
1747 | } | |
1748 | "#, | |
1749 | expect![[r#" | |
1750 | st Buffer [] | |
1751 | fn f() [] | |
1752 | md std [] | |
1753 | tt BufRead (use std::io::BufRead) [requires_import] | |
1754 | st BufReader (use std::io::BufReader) [requires_import] | |
1755 | st BufWriter (use std::io::BufWriter) [requires_import] | |
1756 | "#]], | |
1757 | ); | |
1758 | } | |
1759 | ||
1760 | #[test] | |
1761 | fn completes_struct_with_raw_identifier() { | |
1762 | check_edit( | |
1763 | "type", | |
1764 | r#" | |
1765 | mod m { pub struct r#type {} } | |
1766 | fn main() { | |
1767 | let r#type = m::t$0; | |
1768 | } | |
1769 | "#, | |
1770 | r#" | |
1771 | mod m { pub struct r#type {} } | |
1772 | fn main() { | |
1773 | let r#type = m::r#type; | |
1774 | } | |
1775 | "#, | |
1776 | ) | |
1777 | } | |
1778 | ||
1779 | #[test] | |
1780 | fn completes_fn_with_raw_identifier() { | |
1781 | check_edit( | |
1782 | "type", | |
1783 | r#" | |
1784 | mod m { pub fn r#type {} } | |
1785 | fn main() { | |
1786 | m::t$0 | |
1787 | } | |
1788 | "#, | |
1789 | r#" | |
1790 | mod m { pub fn r#type {} } | |
1791 | fn main() { | |
1792 | m::r#type()$0 | |
1793 | } | |
1794 | "#, | |
1795 | ) | |
1796 | } | |
1797 | ||
1798 | #[test] | |
1799 | fn completes_macro_with_raw_identifier() { | |
1800 | check_edit( | |
1801 | "let!", | |
1802 | r#" | |
1803 | macro_rules! r#let { () => {} } | |
1804 | fn main() { | |
1805 | $0 | |
1806 | } | |
1807 | "#, | |
1808 | r#" | |
1809 | macro_rules! r#let { () => {} } | |
1810 | fn main() { | |
1811 | r#let!($0) | |
1812 | } | |
1813 | "#, | |
1814 | ) | |
1815 | } | |
1816 | ||
1817 | #[test] | |
1818 | fn completes_variant_with_raw_identifier() { | |
1819 | check_edit( | |
1820 | "type", | |
1821 | r#" | |
1822 | enum A { r#type } | |
1823 | fn main() { | |
1824 | let a = A::t$0 | |
1825 | } | |
1826 | "#, | |
1827 | r#" | |
1828 | enum A { r#type } | |
1829 | fn main() { | |
1830 | let a = A::r#type$0 | |
1831 | } | |
1832 | "#, | |
1833 | ) | |
1834 | } | |
1835 | ||
1836 | #[test] | |
1837 | fn completes_field_with_raw_identifier() { | |
1838 | check_edit( | |
1839 | "fn", | |
1840 | r#" | |
1841 | mod r#type { | |
1842 | pub struct r#struct { | |
1843 | pub r#fn: u32 | |
1844 | } | |
1845 | } | |
1846 | ||
1847 | fn main() { | |
1848 | let a = r#type::r#struct {}; | |
1849 | a.$0 | |
1850 | } | |
1851 | "#, | |
1852 | r#" | |
1853 | mod r#type { | |
1854 | pub struct r#struct { | |
1855 | pub r#fn: u32 | |
1856 | } | |
1857 | } | |
1858 | ||
1859 | fn main() { | |
1860 | let a = r#type::r#struct {}; | |
1861 | a.r#fn | |
1862 | } | |
1863 | "#, | |
1864 | ) | |
1865 | } | |
1866 | ||
1867 | #[test] | |
1868 | fn completes_const_with_raw_identifier() { | |
1869 | check_edit( | |
1870 | "type", | |
1871 | r#" | |
1872 | struct r#struct {} | |
1873 | impl r#struct { pub const r#type: u8 = 1; } | |
1874 | fn main() { | |
1875 | r#struct::t$0 | |
1876 | } | |
1877 | "#, | |
1878 | r#" | |
1879 | struct r#struct {} | |
1880 | impl r#struct { pub const r#type: u8 = 1; } | |
1881 | fn main() { | |
1882 | r#struct::r#type | |
1883 | } | |
1884 | "#, | |
1885 | ) | |
1886 | } | |
1887 | ||
1888 | #[test] | |
1889 | fn completes_type_alias_with_raw_identifier() { | |
1890 | check_edit( | |
1891 | "type type", | |
1892 | r#" | |
1893 | struct r#struct {} | |
1894 | trait r#trait { type r#type; } | |
1895 | impl r#trait for r#struct { type t$0 } | |
1896 | "#, | |
1897 | r#" | |
1898 | struct r#struct {} | |
1899 | trait r#trait { type r#type; } | |
1900 | impl r#trait for r#struct { type r#type = $0; } | |
1901 | "#, | |
1902 | ) | |
1903 | } | |
1904 | ||
1905 | #[test] | |
1906 | fn field_access_includes_self() { | |
1907 | check_edit( | |
1908 | "length", | |
1909 | r#" | |
1910 | struct S { | |
1911 | length: i32 | |
1912 | } | |
1913 | ||
1914 | impl S { | |
1915 | fn some_fn(&self) { | |
1916 | let l = len$0 | |
1917 | } | |
1918 | } | |
1919 | "#, | |
1920 | r#" | |
1921 | struct S { | |
1922 | length: i32 | |
1923 | } | |
1924 | ||
1925 | impl S { | |
1926 | fn some_fn(&self) { | |
1927 | let l = self.length | |
1928 | } | |
1929 | } | |
1930 | "#, | |
1931 | ) | |
1932 | } | |
1933 | } |