1//! `render` module provides utilities for rendering completion suggestions2//! into code pieces that will be presented to user.34pub(crate) mod const_;5pub(crate) mod function;6pub(crate) mod literal;7pub(crate) mod macro_;8pub(crate) mod pattern;9pub(crate) mod type_alias;10pub(crate) mod union_literal;11pub(crate) mod variant;1213use hir::{AsAssocItem, HasAttrs, HirDisplay, Impl, ModuleDef, ScopeDef, Type};14use ide_db::text_edit::TextEdit;15use ide_db::{16 RootDatabase, SnippetCap, SymbolKind,17 documentation::{Documentation, HasDocs},18 helpers::item_name,19 imports::import_assets::LocatedImport,20};21use syntax::{AstNode, SmolStr, SyntaxKind, TextRange, ToSmolStr, ast, format_smolstr};2223use crate::{24 CompletionContext, CompletionItem, CompletionItemKind, CompletionItemRefMode,25 CompletionRelevance,26 context::{27 DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext, TypeLocation,28 },29 item::{Builder, CompletionRelevanceTypeMatch},30 render::{31 function::render_fn,32 literal::render_variant_lit,33 macro_::{render_macro, render_macro_pat},34 },35};36/// Interface for data and methods required for items rendering.37#[derive(Debug, Clone)]38pub(crate) struct RenderContext<'a> {39 completion: &'a CompletionContext<'a>,40 is_private_editable: bool,41 import_to_add: Option<LocatedImport>,42 doc_aliases: Vec<SmolStr>,43}4445impl<'a> RenderContext<'a> {46 pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> {47 RenderContext {48 completion,49 is_private_editable: false,50 import_to_add: None,51 doc_aliases: vec![],52 }53 }5455 pub(crate) fn private_editable(mut self, private_editable: bool) -> Self {56 self.is_private_editable = private_editable;57 self58 }5960 pub(crate) fn import_to_add(mut self, import_to_add: Option<LocatedImport>) -> Self {61 self.import_to_add = import_to_add;62 self63 }6465 pub(crate) fn doc_aliases(mut self, doc_aliases: Vec<SmolStr>) -> Self {66 self.doc_aliases = doc_aliases;67 self68 }6970 fn snippet_cap(&self) -> Option<SnippetCap> {71 self.completion.config.snippet_cap72 }7374 fn db(&self) -> &'a RootDatabase {75 self.completion.db76 }7778 fn source_range(&self) -> TextRange {79 self.completion.source_range()80 }8182 fn completion_relevance(&self) -> CompletionRelevance {83 CompletionRelevance {84 is_private_editable: self.is_private_editable,85 requires_import: self.import_to_add.is_some(),86 ..Default::default()87 }88 }8990 fn is_immediately_after_macro_bang(&self) -> bool {91 self.completion.token.kind() == SyntaxKind::BANG92 && self.completion.token.parent().is_some_and(|it| it.kind() == SyntaxKind::MACRO_CALL)93 }9495 /// Whether `def` is deprecated.96 ///97 /// This can happen for two reasons:98 /// - the def is marked with `#[deprecated]`99 /// - the def is an assoc item whose trait is deprecated100 ///101 /// In order to be able to check for the latter, we'd ideally want to `try_as_dyn<_, dyn AsAssocItem>(def)`102 /// (see [`try_as_dyn`][]), but that function is currently unstable. Therefore, we employ a hack instead:103 /// if `def` can be an assoc item, it should be passed to this method as follows:104 /// ```ignore105 /// self.is_deprecated(def, Some(def))106 /// ```107 /// otherwise, it should be passed as:108 /// ```ignore109 /// self.is_deprecated(def, None)110 /// ```111 ///112 /// [`try_as_dyn`]: https://doc.rust-lang.org/std/any/fn.try_as_dyn.html113 fn is_deprecated(&self, def: impl HasAttrs, def_as_assoc_item: Option<hir::AssocItem>) -> bool {114 let db = self.db();115 def.attrs(db).is_deprecated()116 || def_as_assoc_item117 .and_then(|assoc| assoc.container_or_implemented_trait(db))118 .is_some_and(|trait_| {119 self.is_deprecated(trait_, None /* traits can't be assoc items */)120 })121 }122123 // FIXME: remove this124 fn docs(&self, def: impl HasDocs) -> Option<Documentation<'a>> {125 def.docs(self.db())126 }127}128129pub(crate) fn render_field(130 ctx: RenderContext<'_>,131 dot_access: &DotAccess<'_>,132 receiver: Option<SmolStr>,133 field: hir::Field,134 ty: &hir::Type<'_>,135) -> CompletionItem {136 let db = ctx.db();137 let is_deprecated = ctx.is_deprecated(field, None /* fields can't be assoc items */);138 let name = field.name(db);139 let (name, escaped_name) =140 (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr());141 let mut item = CompletionItem::new(142 SymbolKind::Field,143 ctx.source_range(),144 field_with_receiver(receiver.as_deref(), &name),145 ctx.completion.edition,146 );147 item.set_relevance(CompletionRelevance {148 type_match: compute_type_match(ctx.completion, ty),149 exact_name_match: compute_exact_name_match(ctx.completion, &name),150 is_skipping_completion: receiver.is_some(),151 ..CompletionRelevance::default()152 });153 item.detail(ty.display(db, ctx.completion.display_target).to_string())154 .set_documentation(field.docs(db))155 .set_deprecated(is_deprecated)156 .lookup_by(name);157158 let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });159 if !is_field_access || ty.is_fn() || ty.is_closure() {160 let mut builder = TextEdit::builder();161 // Using TextEdit, insert '(' before the struct name and ')' before the162 // dot access, then comes the field name and optionally insert function163 // call parens.164165 builder.replace(166 ctx.source_range(),167 field_with_receiver(receiver.as_deref(), &escaped_name).into(),168 );169170 let expected_fn_type =171 ctx.completion.expected_type.as_ref().is_some_and(|ty| ty.is_fn() || ty.is_closure());172173 if !expected_fn_type174 && let Some(receiver) = &dot_access.receiver175 && let Some(receiver) = ctx.completion.sema.original_ast_node(receiver.clone())176 {177 builder.insert(receiver.syntax().text_range().start(), "(".to_owned());178 builder.insert(ctx.source_range().end(), ")".to_owned());179180 let is_parens_needed = !matches!(dot_access.kind, DotAccessKind::Method);181182 if is_parens_needed {183 builder.insert(ctx.source_range().end(), "()".to_owned());184 }185 }186187 item.text_edit(builder.finish());188 } else {189 item.insert_text(field_with_receiver(receiver.as_deref(), &escaped_name));190 }191 if let Some(receiver) = &dot_access.receiver192 && let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone())193 && let Some(ref_mode) = compute_ref_match(ctx.completion, ty)194 {195 item.ref_match(ref_mode, original.syntax().text_range().start());196 }197 item.doc_aliases(ctx.doc_aliases);198 item.build(db)199}200201fn field_with_receiver(receiver: Option<&str>, field_name: &str) -> SmolStr {202 receiver203 .map_or_else(|| field_name.into(), |receiver| format_smolstr!("{}.{field_name}", receiver))204}205206pub(crate) fn render_tuple_field(207 ctx: RenderContext<'_>,208 receiver: Option<SmolStr>,209 field: usize,210 ty: &hir::Type<'_>,211) -> CompletionItem {212 let mut item = CompletionItem::new(213 SymbolKind::Field,214 ctx.source_range(),215 field_with_receiver(receiver.as_deref(), &field.to_string()),216 ctx.completion.edition,217 );218 item.detail(ty.display(ctx.db(), ctx.completion.display_target).to_string())219 .lookup_by(field.to_string());220 item.set_relevance(CompletionRelevance {221 is_skipping_completion: receiver.is_some(),222 ..ctx.completion_relevance()223 });224 item.build(ctx.db())225}226227pub(crate) fn render_type_inference(228 ty_string: String,229 ctx: &CompletionContext<'_>,230 path_ctx: &PathCompletionCtx<'_>,231) -> CompletionItem {232 let mut builder = CompletionItem::new(233 CompletionItemKind::InferredType,234 ctx.source_range(),235 &ty_string,236 ctx.edition,237 );238 adds_ret_type_arrow(ctx, path_ctx, &mut builder, ty_string);239 builder.set_relevance(CompletionRelevance {240 type_match: Some(CompletionRelevanceTypeMatch::Exact),241 exact_name_match: true,242 ..Default::default()243 });244 builder.build(ctx.db)245}246247pub(crate) fn render_path_resolution(248 ctx: RenderContext<'_>,249 path_ctx: &PathCompletionCtx<'_>,250 local_name: hir::Name,251 resolution: ScopeDef,252) -> Builder {253 render_resolution_path(ctx, path_ctx, local_name, None, resolution)254}255256pub(crate) fn render_pattern_resolution(257 ctx: RenderContext<'_>,258 pattern_ctx: &PatternContext,259 local_name: hir::Name,260 resolution: ScopeDef,261) -> Builder {262 render_resolution_pat(ctx, pattern_ctx, local_name, None, resolution)263}264265pub(crate) fn render_resolution_with_import(266 ctx: RenderContext<'_>,267 path_ctx: &PathCompletionCtx<'_>,268 import_edit: LocatedImport,269) -> Option<Builder> {270 let resolution = ScopeDef::from(import_edit.original_item);271 let local_name = get_import_name(resolution, &ctx, &import_edit)?;272 // This now just renders the alias text, but we need to find the aliases earlier and call this with the alias instead.273 let doc_aliases = ctx.completion.doc_aliases_in_scope(resolution);274 let ctx = ctx.doc_aliases(doc_aliases);275 Some(render_resolution_path(ctx, path_ctx, local_name, Some(import_edit), resolution))276}277278pub(crate) fn render_resolution_with_import_pat(279 ctx: RenderContext<'_>,280 pattern_ctx: &PatternContext,281 import_edit: LocatedImport,282) -> Option<Builder> {283 let resolution = ScopeDef::from(import_edit.original_item);284 let local_name = get_import_name(resolution, &ctx, &import_edit)?;285 Some(render_resolution_pat(ctx, pattern_ctx, local_name, Some(import_edit), resolution))286}287288pub(crate) fn render_expr(289 ctx: &CompletionContext<'_>,290 expr: &hir::term_search::Expr<'_>,291) -> Option<Builder> {292 let mut i = 1;293 let mut snippet_formatter = |ty: &hir::Type<'_>| {294 let arg_name = ty295 .as_adt()296 .map(|adt| stdx::to_lower_snake_case(adt.name(ctx.db).as_str()))297 .unwrap_or_else(|| String::from("_"));298 let res = format!("${{{i}:{arg_name}}}");299 i += 1;300 res301 };302303 let mut label_formatter = |ty: &hir::Type<'_>| {304 ty.as_adt()305 .map(|adt| stdx::to_lower_snake_case(adt.name(ctx.db).as_str()))306 .unwrap_or_else(|| String::from("..."))307 };308309 let cfg = ctx.config.find_path_config(ctx.is_nightly);310311 let label =312 expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.display_target).ok()?;313314 let source_range = match ctx.original_token.parent() {315 Some(node) => match node.ancestors().find_map(ast::Path::cast) {316 Some(path) => path.syntax().text_range(),317 None => node.text_range(),318 },319 None => ctx.source_range(),320 };321322 let mut item =323 CompletionItem::new(CompletionItemKind::Expression, source_range, label, ctx.edition);324325 let snippet = format!(326 "{}$0",327 expr.gen_source_code(&ctx.scope, &mut snippet_formatter, cfg, ctx.display_target).ok()?328 );329 let edit = TextEdit::replace(source_range, snippet);330 item.snippet_edit(ctx.config.snippet_cap?, edit);331 item.documentation(Documentation::new_owned(String::from(332 "Autogenerated expression by term search",333 )));334 item.set_relevance(crate::CompletionRelevance {335 type_match: compute_type_match(ctx, &expr.ty(ctx.db)),336 ..Default::default()337 });338 for trait_ in expr.traits_used(ctx.db) {339 let trait_item = hir::ItemInNs::from(hir::ModuleDef::from(trait_));340 let Some(path) = ctx.module.find_path(ctx.db, trait_item, cfg) else {341 continue;342 };343344 item.add_import(LocatedImport::new_no_completion(path, trait_item, trait_item));345 }346347 Some(item)348}349350fn get_import_name(351 resolution: ScopeDef,352 ctx: &RenderContext<'_>,353 import_edit: &LocatedImport,354) -> Option<hir::Name> {355 // FIXME: Temporary workaround for handling aliased import.356 // This should be removed after we have proper support for importing alias.357 // <https://github.com/rust-lang/rust-analyzer/issues/14079>358359 // If `item_to_import` matches `original_item`, we are importing the item itself (not its parent module).360 // In this case, we can use the last segment of `import_path`, as it accounts for the aliased name.361 if import_edit.item_to_import == import_edit.original_item {362 import_edit.import_path.segments().last().cloned()363 } else {364 scope_def_to_name(resolution, ctx, import_edit)365 }366}367368fn scope_def_to_name(369 resolution: ScopeDef,370 ctx: &RenderContext<'_>,371 import_edit: &LocatedImport,372) -> Option<hir::Name> {373 Some(match resolution {374 ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db),375 ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?,376 ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db),377 _ => item_name(ctx.db(), import_edit.original_item)?,378 })379}380381fn render_resolution_pat(382 ctx: RenderContext<'_>,383 pattern_ctx: &PatternContext,384 local_name: hir::Name,385 import_to_add: Option<LocatedImport>,386 resolution: ScopeDef,387) -> Builder {388 let _p = tracing::info_span!("render_resolution_pat").entered();389 use hir::ModuleDef::*;390391 if let ScopeDef::ModuleDef(Macro(mac)) = resolution {392 let ctx = ctx.import_to_add(import_to_add);393 render_macro_pat(ctx, pattern_ctx, local_name, mac)394 } else {395 render_resolution_simple_(ctx, &local_name, import_to_add, resolution)396 }397}398399fn render_resolution_path(400 ctx: RenderContext<'_>,401 path_ctx: &PathCompletionCtx<'_>,402 local_name: hir::Name,403 import_to_add: Option<LocatedImport>,404 resolution: ScopeDef,405) -> Builder {406 let _p = tracing::info_span!("render_resolution_path").entered();407 use hir::ModuleDef::*;408409 let krate = ctx.completion.display_target;410411 match resolution {412 ScopeDef::ModuleDef(Macro(mac)) => {413 let ctx = ctx.import_to_add(import_to_add);414 return render_macro(ctx, path_ctx, local_name, mac);415 }416 ScopeDef::ModuleDef(Function(func)) => {417 let ctx = ctx.import_to_add(import_to_add);418 return render_fn(ctx, path_ctx, Some(local_name), func);419 }420 ScopeDef::ModuleDef(EnumVariant(var)) => {421 let ctx = ctx.clone().import_to_add(import_to_add.clone());422 if let Some(item) =423 render_variant_lit(ctx, path_ctx, Some(local_name.clone()), var, None)424 {425 return item;426 }427 }428 _ => (),429 }430431 let completion = ctx.completion;432 let module = completion.module;433 let cap = ctx.snippet_cap();434 let db = completion.db;435 let config = completion.config;436 let requires_import = import_to_add.is_some();437438 let name = local_name.display(db, completion.edition).to_smolstr();439 let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution);440 let mut insert_text = name.clone();441442 // Add `<>` for generic types443 let type_path_no_ty_args = matches!(444 path_ctx,445 PathCompletionCtx { kind: PathKind::Type { .. }, has_type_args: false, .. }446 ) && config.callable.is_some();447 if type_path_no_ty_args && let Some(cap) = cap {448 let has_non_default_type_params = match resolution {449 ScopeDef::ModuleDef(hir::ModuleDef::Adt(it)) => it.has_non_default_type_params(db),450 ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(it)) => {451 it.has_non_default_type_params(db)452 }453 _ => false,454 };455456 if has_non_default_type_params {457 cov_mark::hit!(inserts_angle_brackets_for_generics);458 insert_text = format_smolstr!("{insert_text}<$0>");459 item.lookup_by(name.clone())460 .label(SmolStr::from_iter([&name, "<…>"]))461 .trigger_call_info()462 .insert_snippet(cap, ""); // set is snippet463 }464 }465 adds_ret_type_arrow(completion, path_ctx, &mut item, insert_text.into());466467 let mut set_item_relevance = |ty: Type<'_>| {468 if !ty.is_unknown() {469 item.detail(ty.display(db, krate).to_string());470 }471472 item.set_relevance(CompletionRelevance {473 type_match: compute_type_match(completion, &ty),474 exact_name_match: compute_exact_name_match(completion, &name),475 is_local: matches!(resolution, ScopeDef::Local(_)),476 requires_import,477 has_local_inherent_impl: compute_has_local_inherent_impl(db, path_ctx, &ty, module),478 ..CompletionRelevance::default()479 });480481 path_ref_match(completion, path_ctx, &ty, &mut item);482 };483484 match resolution {485 ScopeDef::Local(local) => set_item_relevance(local.ty(db)),486 ScopeDef::ModuleDef(ModuleDef::Adt(adt)) | ScopeDef::AdtSelfType(adt) => {487 set_item_relevance(adt.ty(db))488 }489 // Filtered out above490 ScopeDef::ModuleDef(491 ModuleDef::Function(_) | ModuleDef::EnumVariant(_) | ModuleDef::Macro(_),492 ) => (),493 ScopeDef::ModuleDef(ModuleDef::Const(konst)) => set_item_relevance(konst.ty(db)),494 ScopeDef::ModuleDef(ModuleDef::Static(stat)) => set_item_relevance(stat.ty(db)),495 ScopeDef::ModuleDef(ModuleDef::BuiltinType(bt)) => set_item_relevance(bt.ty(db)),496 ScopeDef::ImplSelfType(imp) => set_item_relevance(imp.self_ty(db)),497 ScopeDef::GenericParam(_)498 | ScopeDef::Label(_)499 | ScopeDef::Unknown500 | ScopeDef::ModuleDef(501 ModuleDef::Trait(_) | ModuleDef::Module(_) | ModuleDef::TypeAlias(_),502 ) => (),503 };504505 item506}507508fn render_resolution_simple_(509 ctx: RenderContext<'_>,510 local_name: &hir::Name,511 import_to_add: Option<LocatedImport>,512 resolution: ScopeDef,513) -> Builder {514 let _p = tracing::info_span!("render_resolution_simple_").entered();515516 let db = ctx.db();517 let ctx = ctx.import_to_add(import_to_add);518 let kind = res_to_kind(resolution);519520 let mut item = CompletionItem::new(521 kind,522 ctx.source_range(),523 local_name.as_str().to_smolstr(),524 ctx.completion.edition,525 );526 item.set_relevance(ctx.completion_relevance())527 .set_documentation(scope_def_docs(db, resolution))528 .set_deprecated(scope_def_is_deprecated(&ctx, resolution));529530 if let Some(import_to_add) = ctx.import_to_add {531 item.add_import(import_to_add);532 }533534 item.doc_aliases(ctx.doc_aliases);535 item536}537538fn res_to_kind(resolution: ScopeDef) -> CompletionItemKind {539 use hir::ModuleDef::*;540 match resolution {541 ScopeDef::Unknown => CompletionItemKind::UnresolvedReference,542 ScopeDef::ModuleDef(Function(_)) => CompletionItemKind::SymbolKind(SymbolKind::Function),543 ScopeDef::ModuleDef(EnumVariant(_)) => CompletionItemKind::SymbolKind(SymbolKind::Variant),544 ScopeDef::ModuleDef(Macro(_)) => CompletionItemKind::SymbolKind(SymbolKind::Macro),545 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module),546 ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt {547 hir::Adt::Struct(_) => SymbolKind::Struct,548 hir::Adt::Union(_) => SymbolKind::Union,549 hir::Adt::Enum(_) => SymbolKind::Enum,550 }),551 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const),552 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static),553 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait),554 ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias),555 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,556 ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param {557 hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam,558 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam,559 hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam,560 }),561 ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),562 ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label),563 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => {564 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)565 }566 }567}568569fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<Documentation<'_>> {570 use hir::ModuleDef::*;571 match resolution {572 ScopeDef::ModuleDef(Module(it)) => it.docs(db),573 ScopeDef::ModuleDef(Adt(it)) => it.docs(db),574 ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(db),575 ScopeDef::ModuleDef(Const(it)) => it.docs(db),576 ScopeDef::ModuleDef(Static(it)) => it.docs(db),577 ScopeDef::ModuleDef(Trait(it)) => it.docs(db),578 ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(db),579 _ => None,580 }581}582583fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> bool {584 let db = ctx.db();585 match resolution {586 ScopeDef::ModuleDef(it) => ctx.is_deprecated(it, it.as_assoc_item(db)),587 ScopeDef::GenericParam(it) => {588 ctx.is_deprecated(it, None /* generic params can't be assoc items */)589 }590 ScopeDef::AdtSelfType(it) => {591 ctx.is_deprecated(it, None /* `Self` can't be an assoc item */)592 }593 _ => false,594 }595}596597pub(crate) fn render_type_keyword_snippet(598 ctx: &CompletionContext<'_>,599 path_ctx: &PathCompletionCtx<'_>,600 label: &str,601 snippet: &str,602) -> Builder {603 let source_range = ctx.source_range();604 let mut item =605 CompletionItem::new(CompletionItemKind::Keyword, source_range, label, ctx.edition);606607 let insert_text = if !snippet.contains('$') {608 item.insert_text(snippet);609 snippet610 } else if let Some(cap) = ctx.config.snippet_cap {611 item.insert_snippet(cap, snippet);612 snippet613 } else {614 label615 };616617 adds_ret_type_arrow(ctx, path_ctx, &mut item, insert_text.to_owned());618 item619}620621fn adds_ret_type_arrow(622 ctx: &CompletionContext<'_>,623 path_ctx: &PathCompletionCtx<'_>,624 item: &mut Builder,625 insert_text: String,626) {627 if let Some((arrow, at)) = path_ctx.required_thin_arrow() {628 let mut edit = TextEdit::builder();629630 edit.insert(at, arrow.to_owned());631 edit.replace(ctx.source_range(), insert_text);632633 item.text_edit(edit.finish()).adds_text(SmolStr::new_static(arrow));634 } else {635 item.insert_text(insert_text);636 }637}638639// FIXME: This checks types without possible coercions which some completions might want to do640fn match_types(641 ctx: &CompletionContext<'_>,642 ty1: &hir::Type<'_>,643 ty2: &hir::Type<'_>,644) -> Option<CompletionRelevanceTypeMatch> {645 if ty1 == ty2 {646 Some(CompletionRelevanceTypeMatch::Exact)647 } else if ty1.could_unify_with(ctx.db, ty2) {648 Some(CompletionRelevanceTypeMatch::CouldUnify)649 } else {650 None651 }652}653654fn compute_type_match(655 ctx: &CompletionContext<'_>,656 completion_ty: &hir::Type<'_>,657) -> Option<CompletionRelevanceTypeMatch> {658 let expected_type = ctx.expected_type.as_ref()?;659660 // We don't ever consider unit type to be an exact type match, since661 // nearly always this is not meaningful to the user.662 if expected_type.is_unit() {663 return None;664 }665666 // &mut ty -> &ty667 if completion_ty.is_mutable_reference()668 && let Some(expected_type) = expected_type.remove_ref()669 && let Some(completion_ty) = completion_ty.remove_ref()670 {671 return match_types(ctx, &expected_type, &completion_ty);672 }673674 match_types(ctx, expected_type, completion_ty)675}676677fn compute_has_local_inherent_impl(678 db: &RootDatabase,679 path_ctx: &PathCompletionCtx<'_>,680 completion_ty: &hir::Type<'_>,681 curr_module: hir::Module,682) -> bool {683 matches!(path_ctx.kind, PathKind::Type { location: TypeLocation::ImplTarget })684 && Impl::all_for_type(db, completion_ty.clone())685 .iter()686 .any(|imp| imp.trait_(db).is_none() && imp.module(db) == curr_module)687}688689fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool {690 ctx.expected_name.as_ref().is_some_and(|name| name.text() == completion_name)691}692693fn compute_ref_match(694 ctx: &CompletionContext<'_>,695 completion_ty: &hir::Type<'_>,696) -> Option<CompletionItemRefMode> {697 let expected_type = ctx.expected_type.as_ref()?;698 let expected_without_ref = expected_type.remove_ref();699 let completion_without_ref = completion_ty.remove_ref();700 if expected_type.could_unify_with(ctx.db, completion_ty) {701 return None;702 }703 if let Some(expected_without_ref) = &expected_without_ref704 && (completion_without_ref.is_none()705 || completion_ty.could_unify_with(ctx.db, expected_without_ref))706 && completion_ty.autoderef(ctx.db).any(|ty| ty == *expected_without_ref)707 {708 cov_mark::hit!(suggest_ref);709 let mutability = if expected_type.is_mutable_reference() {710 hir::Mutability::Mut711 } else {712 hir::Mutability::Shared713 };714 return Some(CompletionItemRefMode::Reference(mutability));715 }716717 if let Some(completion_without_ref) = completion_without_ref718 && completion_without_ref == *expected_type719 && completion_without_ref.is_copy(ctx.db)720 {721 cov_mark::hit!(suggest_deref);722 return Some(CompletionItemRefMode::Dereference);723 }724725 None726}727728fn path_ref_match(729 completion: &CompletionContext<'_>,730 path_ctx: &PathCompletionCtx<'_>,731 ty: &hir::Type<'_>,732 item: &mut Builder,733) {734 if let Some(original_path) = &path_ctx.original_path {735 // At least one char was typed by the user already, in that case look for the original path736 if let Some(original_path) = completion.sema.original_ast_node(original_path.clone())737 && let Some(ref_mode) = compute_ref_match(completion, ty)738 {739 item.ref_match(ref_mode, original_path.syntax().text_range().start());740 }741 } else {742 // completion requested on an empty identifier, there is no path here yet.743 // FIXME: This might create inconsistent completions where we show a ref match in macro inputs744 // as long as nothing was typed yet745 if let Some(ref_mode) = compute_ref_match(completion, ty) {746 item.ref_match(ref_mode, completion.source_range().start());747 }748 }749}750751#[cfg(test)]752mod tests {753 use std::cmp;754755 use expect_test::{Expect, expect};756 use ide_db::SymbolKind;757 use itertools::Itertools;758759 use crate::{760 CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch,761 item::CompletionRelevanceTypeMatch,762 tests::{TEST_CONFIG, check_edit, do_completion, get_all_items},763 };764765 #[track_caller]766 fn check(767 #[rust_analyzer::rust_fixture] ra_fixture: &str,768 kind: impl Into<CompletionItemKind>,769 expect: Expect,770 ) {771 let actual = do_completion(ra_fixture, kind.into());772 expect.assert_debug_eq(&actual);773 }774775 #[track_caller]776 fn check_kinds(777 #[rust_analyzer::rust_fixture] ra_fixture: &str,778 kinds: &[CompletionItemKind],779 expect: Expect,780 ) {781 let actual: Vec<_> =782 kinds.iter().flat_map(|&kind| do_completion(ra_fixture, kind)).collect();783 expect.assert_debug_eq(&actual);784 }785786 #[track_caller]787 fn check_function_relevance(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {788 let actual: Vec<_> =789 do_completion(ra_fixture, CompletionItemKind::SymbolKind(SymbolKind::Method))790 .into_iter()791 .map(|item| (item.detail.unwrap_or_default(), item.relevance.function))792 .collect();793794 expect.assert_debug_eq(&actual);795 }796797 #[track_caller]798 fn check_relevance_for_kinds(799 #[rust_analyzer::rust_fixture] ra_fixture: &str,800 kinds: &[CompletionItemKind],801 expect: Expect,802 ) {803 let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);804 actual.retain(|it| kinds.contains(&it.kind));805 actual.sort_by_key(|it| (cmp::Reverse(it.relevance.score()), it.label.primary.clone()));806 check_relevance_(actual, expect);807 }808809 #[track_caller]810 fn check_relevance(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {811 let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);812 actual.retain(|it| it.kind != CompletionItemKind::Snippet);813 actual.retain(|it| it.kind != CompletionItemKind::Keyword);814 actual.retain(|it| it.kind != CompletionItemKind::BuiltinType);815 actual.sort_by_key(|it| (cmp::Reverse(it.relevance.score()), it.label.primary.clone()));816 check_relevance_(actual, expect);817 }818819 #[track_caller]820 fn check_relevance_(actual: Vec<CompletionItem>, expect: Expect) {821 let actual = actual822 .into_iter()823 .flat_map(|it| {824 let mut items = vec![];825826 let tag = it.kind.tag();827 let relevance = display_relevance(it.relevance);828 items.push(format!(829 "{tag} {} {} {relevance}\n",830 it.label.primary,831 it.label.detail_right.clone().unwrap_or_default(),832 ));833834 if let Some((label, _indel, relevance)) = it.ref_match() {835 let relevance = display_relevance(relevance);836837 items.push(format!("{tag} {label} {relevance}\n"));838 }839840 items841 })842 .collect::<String>();843844 expect.assert_eq(&actual);845846 fn display_relevance(relevance: CompletionRelevance) -> String {847 let relevance_factors = vec![848 (relevance.type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"),849 (850 relevance.type_match == Some(CompletionRelevanceTypeMatch::CouldUnify),851 "type_could_unify",852 ),853 (relevance.exact_name_match, "name"),854 (relevance.is_local, "local"),855 (856 relevance.postfix_match == Some(CompletionRelevancePostfixMatch::Exact),857 "snippet",858 ),859 (relevance.trait_.is_some_and(|it| it.is_op_method), "op_method"),860 (relevance.requires_import, "requires_import"),861 (relevance.has_local_inherent_impl, "has_local_inherent_impl"),862 ]863 .into_iter()864 .filter_map(|(cond, desc)| if cond { Some(desc) } else { None })865 .join("+");866867 format!("[{relevance_factors}]")868 }869 }870871 #[test]872 fn set_struct_type_completion_info() {873 check_relevance(874 r#"875//- /lib.rs crate:dep876877pub mod test_mod_b {878 pub struct Struct {}879}880881pub mod test_mod_a {882 pub struct Struct {}883}884885//- /main.rs crate:main deps:dep886887fn test(input: dep::test_mod_b::Struct) { }888889fn main() {890 test(Struct$0);891}892"#,893 expect![[r#"894 st dep::test_mod_b::Struct {…} dep::test_mod_b::Struct { } [type_could_unify]895 ex dep::test_mod_b::Struct { } [type_could_unify]896 st Struct Struct [type_could_unify+requires_import]897 md dep []898 fn main() fn() []899 fn test(…) fn(Struct) []900 st Struct Struct [requires_import]901 "#]],902 );903 }904905 #[test]906 fn set_union_type_completion_info() {907 check_relevance(908 r#"909//- /lib.rs crate:dep910911pub mod test_mod_b {912 pub union Union {913 a: i32,914 b: i32915 }916}917918pub mod test_mod_a {919 pub enum Union {920 a: i32,921 b: i32922 }923}924925//- /main.rs crate:main deps:dep926927fn test(input: dep::test_mod_b::Union) { }928929fn main() {930 test(Union$0);931}932"#,933 expect![[r#"934 un Union Union [type_could_unify+requires_import]935 md dep []936 fn main() fn() []937 fn test(…) fn(Union) []938 en Union Union [requires_import]939 "#]],940 );941 }942943 #[test]944 fn set_enum_type_completion_info() {945 check_relevance(946 r#"947//- /lib.rs crate:dep948949pub mod test_mod_b {950 pub enum Enum {951 variant952 }953}954955pub mod test_mod_a {956 pub enum Enum {957 variant958 }959}960961//- /main.rs crate:main deps:dep962963fn test(input: dep::test_mod_b::Enum) { }964965fn main() {966 test(Enum$0);967}968"#,969 expect![[r#"970 ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type_could_unify]971 ex dep::test_mod_b::Enum::variant [type_could_unify]972 en Enum Enum [type_could_unify+requires_import]973 md dep []974 fn main() fn() []975 fn test(…) fn(Enum) []976 en Enum Enum [requires_import]977 "#]],978 );979 }980981 #[test]982 fn set_enum_variant_type_completion_info() {983 check_relevance(984 r#"985//- /lib.rs crate:dep986987pub mod test_mod_b {988 pub enum Enum {989 Variant990 }991}992993pub mod test_mod_a {994 pub enum Enum {995 Variant996 }997}998999//- /main.rs crate:main deps:dep10001001fn test(input: dep::test_mod_b::Enum) { }10021003fn main() {1004 test(Variant$0);1005}1006"#,1007 expect![[r#"1008 ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type_could_unify]1009 ex dep::test_mod_b::Enum::Variant [type_could_unify]1010 md dep []1011 fn main() fn() []1012 fn test(…) fn(Enum) []1013 "#]],1014 );1015 }10161017 #[test]1018 fn set_fn_type_completion_info() {1019 check_relevance(1020 r#"1021//- /lib.rs crate:dep10221023pub mod test_mod_b {1024 pub fn function(j: isize) -> i32 {}1025}10261027pub mod test_mod_a {1028 pub fn function(i: usize) -> i32 {}1029}10301031//- /main.rs crate:main deps:dep10321033fn test(input: fn(usize) -> i32) { }10341035fn main() {1036 test(function$0);1037}1038"#,1039 expect![[r#"1040 md dep []1041 fn main() fn() []1042 fn test(…) fn(fn(usize) -> i32) []1043 fn function fn(usize) -> i32 [requires_import]1044 fn function(…) fn(isize) -> i32 [requires_import]1045 "#]],1046 );1047 }10481049 #[test]1050 fn set_const_type_completion_info() {1051 check_relevance(1052 r#"1053//- /lib.rs crate:dep10541055pub mod test_mod_b {1056 pub const CONST: i32 = 1;1057}10581059pub mod test_mod_a {1060 pub const CONST: i64 = 2;1061}10621063//- /main.rs crate:main deps:dep10641065fn test(input: i32) { }10661067fn main() {1068 test(CONST$0);1069}1070"#,1071 expect![[r#"1072 ct CONST i32 [type_could_unify+requires_import]1073 md dep []1074 fn main() fn() []1075 fn test(…) fn(i32) []1076 ct CONST i64 [requires_import]1077 "#]],1078 );1079 }10801081 #[test]1082 fn set_static_type_completion_info() {1083 check_relevance(1084 r#"1085//- /lib.rs crate:dep10861087pub mod test_mod_b {1088 pub static STATIC: i32 = 5;1089}10901091pub mod test_mod_a {1092 pub static STATIC: i64 = 5;1093}10941095//- /main.rs crate:main deps:dep10961097fn test(input: i32) { }10981099fn main() {1100 test(STATIC$0);1101}1102"#,1103 expect![[r#"1104 sc STATIC i32 [type_could_unify+requires_import]1105 md dep []1106 fn main() fn() []1107 fn test(…) fn(i32) []1108 sc STATIC i64 [requires_import]1109 "#]],1110 );1111 }11121113 #[test]1114 fn set_self_type_completion_info_with_params() {1115 check_relevance(1116 r#"1117//- /lib.rs crate:dep1118pub struct Struct;11191120impl Struct {1121 pub fn Function(&self, input: i32) -> bool {1122 false1123 }1124}112511261127//- /main.rs crate:main deps:dep11281129use dep::Struct;113011311132fn test(input: fn(&dep::Struct, i32) -> bool) { }11331134fn main() {1135 test(Struct::Function$0);1136}11371138"#,1139 expect![[r#"1140 me Function fn(&self, i32) -> bool []1141 "#]],1142 );1143 }11441145 #[test]1146 fn set_self_type_completion_info() {1147 check_relevance(1148 r#"1149//- /main.rs crate:main11501151struct Struct;11521153impl Struct {1154fn test(&self) {1155 func(Self$0);1156 }1157}11581159fn func(input: Struct) { }11601161"#,1162 expect![[r#"1163 st Self Self [type]1164 st Struct Struct [type]1165 sp Self Struct [type]1166 st Struct Struct [type]1167 ex Struct [type]1168 lc self &Struct [local]1169 fn func(…) fn(Struct) []1170 me self.test() fn(&self) []1171 "#]],1172 );1173 }11741175 #[test]1176 fn set_builtin_type_completion_info() {1177 check_relevance(1178 r#"1179//- /main.rs crate:main11801181fn test(input: bool) { }1182 pub Input: bool = false;11831184fn main() {1185 let input = false;1186 let inputbad = 3;1187 test(inp$0);1188}1189"#,1190 expect![[r#"1191 lc input bool [type+name+local]1192 ex false [type]1193 ex input [type]1194 ex true [type]1195 lc inputbad i32 [local]1196 fn main() fn() []1197 fn test(…) fn(bool) []1198 "#]],1199 );1200 }12011202 #[test]1203 fn enum_detail_includes_record_fields() {1204 check(1205 r#"1206enum Foo { Foo { x: i32, y: i32 } }12071208fn main() { Foo::Fo$0 }1209"#,1210 SymbolKind::Variant,1211 expect![[r#"1212 [1213 CompletionItem {1214 label: "Foo {…}",1215 detail_left: None,1216 detail_right: Some(1217 "Foo { x: i32, y: i32 }",1218 ),1219 source_range: 54..56,1220 delete: 54..56,1221 insert: "Foo { x: ${1:()}, y: ${2:()} }$0",1222 kind: SymbolKind(1223 Variant,1224 ),1225 lookup: "Foo{}",1226 detail: "Foo { x: i32, y: i32 }",1227 relevance: CompletionRelevance {1228 exact_name_match: false,1229 type_match: None,1230 is_local: false,1231 trait_: None,1232 is_name_already_imported: false,1233 requires_import: false,1234 is_private_editable: false,1235 postfix_match: None,1236 function: Some(1237 CompletionRelevanceFn {1238 has_params: true,1239 has_self_param: false,1240 return_type: DirectConstructor,1241 },1242 ),1243 is_skipping_completion: false,1244 has_local_inherent_impl: false,1245 },1246 trigger_call_info: true,1247 },1248 ]1249 "#]],1250 );1251 }12521253 #[test]1254 fn enum_detail_includes_tuple_fields() {1255 check(1256 r#"1257enum Foo { Foo (i32, i32) }12581259fn main() { Foo::Fo$0 }1260"#,1261 SymbolKind::Variant,1262 expect![[r#"1263 [1264 CompletionItem {1265 label: "Foo(…)",1266 detail_left: None,1267 detail_right: Some(1268 "Foo(i32, i32)",1269 ),1270 source_range: 46..48,1271 delete: 46..48,1272 insert: "Foo(${1:()}, ${2:()})$0",1273 kind: SymbolKind(1274 Variant,1275 ),1276 lookup: "Foo()",1277 detail: "Foo(i32, i32)",1278 relevance: CompletionRelevance {1279 exact_name_match: false,1280 type_match: None,1281 is_local: false,1282 trait_: None,1283 is_name_already_imported: false,1284 requires_import: false,1285 is_private_editable: false,1286 postfix_match: None,1287 function: Some(1288 CompletionRelevanceFn {1289 has_params: true,1290 has_self_param: false,1291 return_type: DirectConstructor,1292 },1293 ),1294 is_skipping_completion: false,1295 has_local_inherent_impl: false,1296 },1297 trigger_call_info: true,1298 },1299 ]1300 "#]],1301 );1302 }13031304 #[test]1305 fn fn_detail_includes_args_and_return_type() {1306 check(1307 r#"1308fn foo<T>(a: u32, b: u32, t: T) -> (u32, T) { (a, t) }13091310fn main() { fo$0 }1311"#,1312 SymbolKind::Function,1313 expect![[r#"1314 [1315 CompletionItem {1316 label: "foo(…)",1317 detail_left: None,1318 detail_right: Some(1319 "fn(u32, u32, T) -> (u32, T)",1320 ),1321 source_range: 68..70,1322 delete: 68..70,1323 insert: "foo(${1:a}, ${2:b}, ${3:t})$0",1324 kind: SymbolKind(1325 Function,1326 ),1327 lookup: "foo",1328 detail: "fn(u32, u32, T) -> (u32, T)",1329 trigger_call_info: true,1330 },1331 CompletionItem {1332 label: "main()",1333 detail_left: None,1334 detail_right: Some(1335 "fn()",1336 ),1337 source_range: 68..70,1338 delete: 68..70,1339 insert: "main();$0",1340 kind: SymbolKind(1341 Function,1342 ),1343 lookup: "main",1344 detail: "fn()",1345 },1346 ]1347 "#]],1348 );1349 }13501351 #[test]1352 fn fn_detail_includes_variadics() {1353 check(1354 r#"1355unsafe extern "C" fn foo(a: u32, b: u32, ...) {}13561357fn main() { fo$0 }1358"#,1359 SymbolKind::Function,1360 expect![[r#"1361 [1362 CompletionItem {1363 label: "foo(…)",1364 detail_left: None,1365 detail_right: Some(1366 "unsafe fn(u32, u32, ...)",1367 ),1368 source_range: 62..64,1369 delete: 62..64,1370 insert: "foo(${1:a}, ${2:b});$0",1371 kind: SymbolKind(1372 Function,1373 ),1374 lookup: "foo",1375 detail: "unsafe fn(u32, u32, ...)",1376 trigger_call_info: true,1377 },1378 CompletionItem {1379 label: "main()",1380 detail_left: None,1381 detail_right: Some(1382 "fn()",1383 ),1384 source_range: 62..64,1385 delete: 62..64,1386 insert: "main();$0",1387 kind: SymbolKind(1388 Function,1389 ),1390 lookup: "main",1391 detail: "fn()",1392 },1393 ]1394 "#]],1395 );1396 }13971398 #[test]1399 fn enum_detail_just_name_for_unit() {1400 check(1401 r#"1402enum Foo { Foo }14031404fn main() { Foo::Fo$0 }1405"#,1406 SymbolKind::Variant,1407 expect![[r#"1408 [1409 CompletionItem {1410 label: "Foo",1411 detail_left: None,1412 detail_right: Some(1413 "Foo",1414 ),1415 source_range: 35..37,1416 delete: 35..37,1417 insert: "Foo$0",1418 kind: SymbolKind(1419 Variant,1420 ),1421 detail: "Foo",1422 relevance: CompletionRelevance {1423 exact_name_match: false,1424 type_match: None,1425 is_local: false,1426 trait_: None,1427 is_name_already_imported: false,1428 requires_import: false,1429 is_private_editable: false,1430 postfix_match: None,1431 function: Some(1432 CompletionRelevanceFn {1433 has_params: false,1434 has_self_param: false,1435 return_type: DirectConstructor,1436 },1437 ),1438 is_skipping_completion: false,1439 has_local_inherent_impl: false,1440 },1441 trigger_call_info: true,1442 },1443 ]1444 "#]],1445 );1446 }14471448 #[test]1449 fn lookup_enums_by_two_qualifiers() {1450 check_kinds(1451 r#"1452mod m {1453 pub enum Spam { Foo, Bar(i32) }1454}1455fn main() { let _: m::Spam = S$0 }1456"#,1457 &[1458 CompletionItemKind::SymbolKind(SymbolKind::Function),1459 CompletionItemKind::SymbolKind(SymbolKind::Module),1460 CompletionItemKind::SymbolKind(SymbolKind::Variant),1461 ],1462 expect![[r#"1463 [1464 CompletionItem {1465 label: "main()",1466 detail_left: None,1467 detail_right: Some(1468 "fn()",1469 ),1470 source_range: 75..76,1471 delete: 75..76,1472 insert: "main();$0",1473 kind: SymbolKind(1474 Function,1475 ),1476 lookup: "main",1477 detail: "fn()",1478 },1479 CompletionItem {1480 label: "m",1481 detail_left: None,1482 detail_right: None,1483 source_range: 75..76,1484 delete: 75..76,1485 insert: "m",1486 kind: SymbolKind(1487 Module,1488 ),1489 },1490 CompletionItem {1491 label: "m::Spam::Bar(…)",1492 detail_left: None,1493 detail_right: Some(1494 "m::Spam::Bar(i32)",1495 ),1496 source_range: 75..76,1497 delete: 75..76,1498 insert: "m::Spam::Bar(${1:()})$0",1499 kind: SymbolKind(1500 Variant,1501 ),1502 lookup: "Spam::Bar()",1503 detail: "m::Spam::Bar(i32)",1504 relevance: CompletionRelevance {1505 exact_name_match: false,1506 type_match: Some(1507 Exact,1508 ),1509 is_local: false,1510 trait_: None,1511 is_name_already_imported: false,1512 requires_import: false,1513 is_private_editable: false,1514 postfix_match: None,1515 function: Some(1516 CompletionRelevanceFn {1517 has_params: true,1518 has_self_param: false,1519 return_type: DirectConstructor,1520 },1521 ),1522 is_skipping_completion: false,1523 has_local_inherent_impl: false,1524 },1525 trigger_call_info: true,1526 },1527 CompletionItem {1528 label: "m::Spam::Foo",1529 detail_left: None,1530 detail_right: Some(1531 "m::Spam::Foo",1532 ),1533 source_range: 75..76,1534 delete: 75..76,1535 insert: "m::Spam::Foo$0",1536 kind: SymbolKind(1537 Variant,1538 ),1539 lookup: "Spam::Foo",1540 detail: "m::Spam::Foo",1541 relevance: CompletionRelevance {1542 exact_name_match: false,1543 type_match: Some(1544 Exact,1545 ),1546 is_local: false,1547 trait_: None,1548 is_name_already_imported: false,1549 requires_import: false,1550 is_private_editable: false,1551 postfix_match: None,1552 function: Some(1553 CompletionRelevanceFn {1554 has_params: false,1555 has_self_param: false,1556 return_type: DirectConstructor,1557 },1558 ),1559 is_skipping_completion: false,1560 has_local_inherent_impl: false,1561 },1562 trigger_call_info: true,1563 },1564 ]1565 "#]],1566 )1567 }15681569 #[test]1570 fn sets_deprecated_flag_in_items() {1571 check(1572 r#"1573#[deprecated]1574mod something_deprecated {}15751576fn main() { som$0 }1577"#,1578 SymbolKind::Module,1579 expect![[r#"1580 [1581 CompletionItem {1582 label: "something_deprecated",1583 detail_left: None,1584 detail_right: None,1585 source_range: 55..58,1586 delete: 55..58,1587 insert: "something_deprecated",1588 kind: SymbolKind(1589 Module,1590 ),1591 deprecated: true,1592 },1593 ]1594 "#]],1595 );15961597 check(1598 r#"1599#[deprecated]1600fn something_deprecated() {}16011602fn main() { som$0 }1603"#,1604 SymbolKind::Function,1605 expect![[r#"1606 [1607 CompletionItem {1608 label: "main()",1609 detail_left: None,1610 detail_right: Some(1611 "fn()",1612 ),1613 source_range: 56..59,1614 delete: 56..59,1615 insert: "main();$0",1616 kind: SymbolKind(1617 Function,1618 ),1619 lookup: "main",1620 detail: "fn()",1621 },1622 CompletionItem {1623 label: "something_deprecated()",1624 detail_left: None,1625 detail_right: Some(1626 "fn()",1627 ),1628 source_range: 56..59,1629 delete: 56..59,1630 insert: "something_deprecated();$0",1631 kind: SymbolKind(1632 Function,1633 ),1634 lookup: "something_deprecated",1635 detail: "fn()",1636 deprecated: true,1637 },1638 ]1639 "#]],1640 );16411642 check(1643 r#"1644#[deprecated]1645struct A;16461647fn main() { A$0 }1648"#,1649 SymbolKind::Struct,1650 expect![[r#"1651 [1652 CompletionItem {1653 label: "A",1654 detail_left: None,1655 detail_right: Some(1656 "A",1657 ),1658 source_range: 37..38,1659 delete: 37..38,1660 insert: "A",1661 kind: SymbolKind(1662 Struct,1663 ),1664 detail: "A",1665 deprecated: true,1666 },1667 ]1668 "#]],1669 );16701671 check(1672 r#"1673#[deprecated]1674enum A {}16751676fn main() { A$0 }1677"#,1678 SymbolKind::Enum,1679 expect![[r#"1680 [1681 CompletionItem {1682 label: "A",1683 detail_left: None,1684 detail_right: Some(1685 "A",1686 ),1687 source_range: 37..38,1688 delete: 37..38,1689 insert: "A",1690 kind: SymbolKind(1691 Enum,1692 ),1693 detail: "A",1694 deprecated: true,1695 },1696 ]1697 "#]],1698 );16991700 check(1701 r#"1702enum A {1703 Okay,1704 #[deprecated]1705 Old,1706}17071708fn main() { A::$0 }1709"#,1710 SymbolKind::Variant,1711 expect![[r#"1712 [1713 CompletionItem {1714 label: "Okay",1715 detail_left: None,1716 detail_right: Some(1717 "Okay",1718 ),1719 source_range: 64..64,1720 delete: 64..64,1721 insert: "Okay$0",1722 kind: SymbolKind(1723 Variant,1724 ),1725 detail: "Okay",1726 relevance: CompletionRelevance {1727 exact_name_match: false,1728 type_match: None,1729 is_local: false,1730 trait_: None,1731 is_name_already_imported: false,1732 requires_import: false,1733 is_private_editable: false,1734 postfix_match: None,1735 function: Some(1736 CompletionRelevanceFn {1737 has_params: false,1738 has_self_param: false,1739 return_type: DirectConstructor,1740 },1741 ),1742 is_skipping_completion: false,1743 has_local_inherent_impl: false,1744 },1745 trigger_call_info: true,1746 },1747 CompletionItem {1748 label: "Old",1749 detail_left: None,1750 detail_right: Some(1751 "Old",1752 ),1753 source_range: 64..64,1754 delete: 64..64,1755 insert: "Old$0",1756 kind: SymbolKind(1757 Variant,1758 ),1759 detail: "Old",1760 deprecated: true,1761 relevance: CompletionRelevance {1762 exact_name_match: false,1763 type_match: None,1764 is_local: false,1765 trait_: None,1766 is_name_already_imported: false,1767 requires_import: false,1768 is_private_editable: false,1769 postfix_match: None,1770 function: Some(1771 CompletionRelevanceFn {1772 has_params: false,1773 has_self_param: false,1774 return_type: DirectConstructor,1775 },1776 ),1777 is_skipping_completion: false,1778 has_local_inherent_impl: false,1779 },1780 trigger_call_info: true,1781 },1782 ]1783 "#]],1784 );17851786 check(1787 r#"1788#[deprecated]1789const A: i32 = 0;17901791fn main() { A$0 }1792"#,1793 SymbolKind::Const,1794 expect![[r#"1795 [1796 CompletionItem {1797 label: "A",1798 detail_left: None,1799 detail_right: Some(1800 "i32",1801 ),1802 source_range: 45..46,1803 delete: 45..46,1804 insert: "A",1805 kind: SymbolKind(1806 Const,1807 ),1808 detail: "i32",1809 deprecated: true,1810 },1811 ]1812 "#]],1813 );18141815 check(1816 r#"1817#[deprecated]1818static A: i32 = 0;18191820fn main() { A$0 }1821"#,1822 SymbolKind::Static,1823 expect![[r#"1824 [1825 CompletionItem {1826 label: "A",1827 detail_left: None,1828 detail_right: Some(1829 "i32",1830 ),1831 source_range: 46..47,1832 delete: 46..47,1833 insert: "A",1834 kind: SymbolKind(1835 Static,1836 ),1837 detail: "i32",1838 deprecated: true,1839 },1840 ]1841 "#]],1842 );18431844 check(1845 r#"1846#[deprecated]1847trait A {}18481849impl A$01850"#,1851 SymbolKind::Trait,1852 expect![[r#"1853 [1854 CompletionItem {1855 label: "A",1856 detail_left: None,1857 detail_right: None,1858 source_range: 31..32,1859 delete: 31..32,1860 insert: "A",1861 kind: SymbolKind(1862 Trait,1863 ),1864 deprecated: true,1865 },1866 ]1867 "#]],1868 );18691870 check(1871 r#"1872#[deprecated]1873type A = i32;18741875fn main() { A$0 }1876"#,1877 SymbolKind::TypeAlias,1878 expect![[r#"1879 [1880 CompletionItem {1881 label: "A",1882 detail_left: None,1883 detail_right: None,1884 source_range: 41..42,1885 delete: 41..42,1886 insert: "A",1887 kind: SymbolKind(1888 TypeAlias,1889 ),1890 deprecated: true,1891 },1892 ]1893 "#]],1894 );18951896 check(1897 r#"1898#[deprecated]1899macro_rules! a { _ => {}}19001901fn main() { a$0 }1902"#,1903 SymbolKind::Macro,1904 expect![[r#"1905 [1906 CompletionItem {1907 label: "a!(…)",1908 detail_left: None,1909 detail_right: Some(1910 "macro_rules! a",1911 ),1912 source_range: 53..54,1913 delete: 53..54,1914 insert: "a!($0)",1915 kind: SymbolKind(1916 Macro,1917 ),1918 lookup: "a!",1919 detail: "macro_rules! a",1920 deprecated: true,1921 },1922 ]1923 "#]],1924 );19251926 check(1927 r#"1928struct A { #[deprecated] the_field: u32 }19291930fn main() { A { the$0 } }1931"#,1932 SymbolKind::Field,1933 expect![[r#"1934 [1935 CompletionItem {1936 label: "the_field",1937 detail_left: None,1938 detail_right: Some(1939 "u32",1940 ),1941 source_range: 59..62,1942 delete: 59..62,1943 insert: "the_field",1944 kind: SymbolKind(1945 Field,1946 ),1947 detail: "u32",1948 deprecated: true,1949 relevance: CompletionRelevance {1950 exact_name_match: false,1951 type_match: Some(1952 CouldUnify,1953 ),1954 is_local: false,1955 trait_: None,1956 is_name_already_imported: false,1957 requires_import: false,1958 is_private_editable: false,1959 postfix_match: None,1960 function: None,1961 is_skipping_completion: false,1962 has_local_inherent_impl: false,1963 },1964 },1965 ]1966 "#]],1967 );1968 }19691970 #[test]1971 fn renders_docs() {1972 check_kinds(1973 r#"1974struct S {1975 /// Field docs1976 foo:1977}1978impl S {1979 /// Method docs1980 fn bar(self) { self.$0 }1981}"#,1982 &[1983 CompletionItemKind::SymbolKind(SymbolKind::Method),1984 CompletionItemKind::SymbolKind(SymbolKind::Field),1985 ],1986 expect![[r#"1987 [1988 CompletionItem {1989 label: "bar()",1990 detail_left: None,1991 detail_right: Some(1992 "fn(self)",1993 ),1994 source_range: 94..94,1995 delete: 94..94,1996 insert: "bar();$0",1997 kind: SymbolKind(1998 Method,1999 ),2000 lookup: "bar",
Findings
✓ No findings reported for this file.