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, 'db> {39 completion: &'a CompletionContext<'a, 'db>,40 is_private_editable: bool,41 import_to_add: Option<LocatedImport>,42 doc_aliases: Vec<SmolStr>,43}4445impl<'a, 'db> RenderContext<'a, 'db> {46 pub(crate) fn new(completion: &'a CompletionContext<'a, 'db>) -> RenderContext<'a, 'db> {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) -> &'db 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 /// Whether an enum variant should be rendered as deprecated.124 ///125 /// A variant inherits deprecation from its parent enum, matching rustc's126 /// behavior where `#[deprecated]` on an enum applies to its variants.127 fn is_variant_deprecated(&self, variant: hir::EnumVariant) -> bool {128 let db = self.db();129 variant.attrs(db).is_deprecated() || variant.parent_enum(db).attrs(db).is_deprecated()130 }131132 // FIXME: remove this133 fn docs(&self, def: impl HasDocs) -> Option<Documentation<'a>> {134 def.docs(self.db())135 }136}137138pub(crate) fn render_field(139 ctx: RenderContext<'_, '_>,140 dot_access: &DotAccess<'_>,141 receiver: Option<SmolStr>,142 field: hir::Field,143 ty: &hir::Type<'_>,144) -> CompletionItem {145 let db = ctx.db();146 let is_deprecated = ctx.is_deprecated(field, None /* fields can't be assoc items */);147 let name = field.name(db);148 let (name, escaped_name) =149 (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr());150 let mut item = CompletionItem::new(151 SymbolKind::Field,152 ctx.source_range(),153 field_with_receiver(receiver.as_deref(), &name),154 ctx.completion.edition,155 );156 item.set_relevance(CompletionRelevance {157 type_match: compute_type_match(ctx.completion, ty),158 exact_name_match: compute_exact_name_match(ctx.completion, &name),159 is_skipping_completion: receiver.is_some(),160 ..CompletionRelevance::default()161 });162 item.detail(ty.display(db, ctx.completion.display_target).to_string())163 .set_documentation(field.docs(db))164 .set_deprecated(is_deprecated)165 .lookup_by(name);166167 let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });168 if !is_field_access || ty.is_fn() || ty.is_closure() {169 let mut builder = TextEdit::builder();170 // Using TextEdit, insert '(' before the struct name and ')' before the171 // dot access, then comes the field name and optionally insert function172 // call parens.173174 builder.replace(175 ctx.source_range(),176 field_with_receiver(receiver.as_deref(), &escaped_name).into(),177 );178179 let expected_fn_type =180 ctx.completion.expected_type.as_ref().is_some_and(|ty| ty.is_fn() || ty.is_closure());181182 if !expected_fn_type183 && let Some(receiver) = &dot_access.receiver184 && let Some(receiver) = ctx.completion.sema.original_range_opt(receiver.syntax())185 {186 builder.insert(receiver.range.start(), "(".to_owned());187 builder.insert(ctx.source_range().end(), ")".to_owned());188189 let is_parens_needed = !matches!(dot_access.kind, DotAccessKind::Method);190191 if is_parens_needed {192 builder.insert(ctx.source_range().end(), "()".to_owned());193 }194 }195196 item.text_edit(builder.finish());197 } else {198 item.insert_text(field_with_receiver(receiver.as_deref(), &escaped_name));199 }200 if let Some(receiver) = &dot_access.receiver201 && let Some(original) = ctx.completion.sema.original_range_opt(receiver.syntax())202 && let Some(ref_mode) = compute_ref_match(ctx.completion, ty)203 {204 item.ref_match(ref_mode, original.range.start());205 }206 item.doc_aliases(ctx.doc_aliases);207 item.build(db)208}209210fn field_with_receiver(receiver: Option<&str>, field_name: &str) -> SmolStr {211 receiver212 .map_or_else(|| field_name.into(), |receiver| format_smolstr!("{}.{field_name}", receiver))213}214215pub(crate) fn render_tuple_field(216 ctx: RenderContext<'_, '_>,217 receiver: Option<SmolStr>,218 field: usize,219 ty: &hir::Type<'_>,220) -> CompletionItem {221 let mut item = CompletionItem::new(222 SymbolKind::Field,223 ctx.source_range(),224 field_with_receiver(receiver.as_deref(), &field.to_string()),225 ctx.completion.edition,226 );227 item.detail(ty.display(ctx.db(), ctx.completion.display_target).to_string())228 .lookup_by(field.to_string());229 item.set_relevance(CompletionRelevance {230 is_skipping_completion: receiver.is_some(),231 ..ctx.completion_relevance()232 });233 item.build(ctx.db())234}235236pub(crate) fn render_type_inference(237 ty_string: String,238 ctx: &CompletionContext<'_, '_>,239 path_ctx: &PathCompletionCtx<'_>,240) -> CompletionItem {241 let mut builder = CompletionItem::new(242 CompletionItemKind::InferredType,243 ctx.source_range(),244 &ty_string,245 ctx.edition,246 );247 adds_ret_type_arrow(ctx, path_ctx, &mut builder, ty_string);248 builder.set_relevance(CompletionRelevance {249 type_match: Some(CompletionRelevanceTypeMatch::Exact),250 exact_name_match: true,251 ..Default::default()252 });253 builder.build(ctx.db)254}255256pub(crate) fn render_path_resolution<'db>(257 ctx: RenderContext<'_, 'db>,258 path_ctx: &PathCompletionCtx<'_>,259 local_name: hir::Name,260 resolution: ScopeDef<'db>,261) -> Builder {262 render_resolution_path(ctx, path_ctx, local_name, None, resolution)263}264265pub(crate) fn render_pattern_resolution<'db>(266 ctx: RenderContext<'_, 'db>,267 pattern_ctx: &PatternContext,268 local_name: hir::Name,269 resolution: ScopeDef<'db>,270) -> Builder {271 render_resolution_pat(ctx, pattern_ctx, local_name, None, resolution)272}273274pub(crate) fn render_resolution_with_import(275 ctx: RenderContext<'_, '_>,276 path_ctx: &PathCompletionCtx<'_>,277 import_edit: LocatedImport,278) -> Option<Builder> {279 let resolution = ScopeDef::from(import_edit.original_item);280 let local_name = get_import_name(resolution, &ctx, &import_edit)?;281 // This now just renders the alias text, but we need to find the aliases earlier and call this with the alias instead.282 let doc_aliases = ctx.completion.doc_aliases_in_scope(resolution);283 let ctx = ctx.doc_aliases(doc_aliases);284 Some(render_resolution_path(ctx, path_ctx, local_name, Some(import_edit), resolution))285}286287pub(crate) fn render_resolution_with_import_pat(288 ctx: RenderContext<'_, '_>,289 pattern_ctx: &PatternContext,290 import_edit: LocatedImport,291) -> Option<Builder> {292 let resolution = ScopeDef::from(import_edit.original_item);293 let local_name = get_import_name(resolution, &ctx, &import_edit)?;294 Some(render_resolution_pat(ctx, pattern_ctx, local_name, Some(import_edit), resolution))295}296297pub(crate) fn render_expr<'db>(298 ctx: &CompletionContext<'_, 'db>,299 expr: &hir::term_search::Expr<'db>,300) -> Option<Builder> {301 let mut i = 1;302 let mut snippet_formatter = |ty: &hir::Type<'_>| {303 let arg_name = ty304 .as_adt()305 .map(|adt| stdx::to_lower_snake_case(adt.name(ctx.db).as_str()))306 .unwrap_or_else(|| String::from("_"));307 let res = format!("${{{i}:{arg_name}}}");308 i += 1;309 res310 };311312 let mut label_formatter = |ty: &hir::Type<'_>| {313 ty.as_adt()314 .map(|adt| stdx::to_lower_snake_case(adt.name(ctx.db).as_str()))315 .unwrap_or_else(|| String::from("..."))316 };317318 let cfg = ctx.config.find_path_config(ctx.is_nightly);319320 let label =321 expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.display_target).ok()?;322323 let source_range = match ctx.original_token.parent() {324 Some(node) => match node.ancestors().find_map(ast::Path::cast) {325 Some(path) => path.syntax().text_range(),326 None => node.text_range(),327 },328 None => ctx.source_range(),329 };330331 let mut item =332 CompletionItem::new(CompletionItemKind::Expression, source_range, label, ctx.edition);333334 let snippet = format!(335 "{}$0",336 expr.gen_source_code(&ctx.scope, &mut snippet_formatter, cfg, ctx.display_target).ok()?337 );338 let edit = TextEdit::replace(source_range, snippet);339 item.snippet_edit(ctx.config.snippet_cap?, edit);340 item.documentation(Documentation::new_owned(String::from(341 "Autogenerated expression by term search",342 )));343 item.set_relevance(crate::CompletionRelevance {344 type_match: compute_type_match(ctx, &ctx.rebase_ty(&expr.ty(ctx.db))),345 ..Default::default()346 });347 for trait_ in expr.traits_used(ctx.db) {348 let trait_item = hir::ItemInNs::from(hir::ModuleDef::from(trait_));349 let Some(path) = ctx.module.find_path(ctx.db, trait_item, cfg) else {350 continue;351 };352353 item.add_import(LocatedImport::new_no_completion(path, trait_item, trait_item));354 }355356 Some(item)357}358359fn get_import_name<'db>(360 resolution: ScopeDef<'db>,361 ctx: &RenderContext<'_, 'db>,362 import_edit: &LocatedImport,363) -> Option<hir::Name> {364 // FIXME: Temporary workaround for handling aliased import.365 // This should be removed after we have proper support for importing alias.366 // <https://github.com/rust-lang/rust-analyzer/issues/14079>367368 // If `item_to_import` matches `original_item`, we are importing the item itself (not its parent module).369 // In this case, we can use the last segment of `import_path`, as it accounts for the aliased name.370 if import_edit.item_to_import == import_edit.original_item {371 import_edit.import_path.segments().last().cloned()372 } else {373 scope_def_to_name(resolution, ctx, import_edit)374 }375}376377fn scope_def_to_name<'db>(378 resolution: ScopeDef<'db>,379 ctx: &RenderContext<'_, 'db>,380 import_edit: &LocatedImport,381) -> Option<hir::Name> {382 Some(match resolution {383 ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db),384 ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?,385 ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db),386 _ => item_name(ctx.db(), import_edit.original_item)?,387 })388}389390fn render_resolution_pat<'db>(391 ctx: RenderContext<'_, 'db>,392 pattern_ctx: &PatternContext,393 local_name: hir::Name,394 import_to_add: Option<LocatedImport>,395 resolution: ScopeDef<'db>,396) -> Builder {397 let _p = tracing::info_span!("render_resolution_pat").entered();398 use hir::ModuleDef::*;399400 if let ScopeDef::ModuleDef(Macro(mac)) = resolution {401 let ctx = ctx.import_to_add(import_to_add);402 render_macro_pat(ctx, pattern_ctx, local_name, mac)403 } else {404 render_resolution_simple_(ctx, &local_name, import_to_add, resolution)405 }406}407408fn render_resolution_path<'db>(409 ctx: RenderContext<'_, 'db>,410 path_ctx: &PathCompletionCtx<'_>,411 local_name: hir::Name,412 import_to_add: Option<LocatedImport>,413 resolution: ScopeDef<'db>,414) -> Builder {415 let _p = tracing::info_span!("render_resolution_path").entered();416 use hir::ModuleDef::*;417418 let krate = ctx.completion.display_target;419420 match resolution {421 ScopeDef::ModuleDef(Macro(mac)) => {422 let ctx = ctx.import_to_add(import_to_add);423 return render_macro(ctx, path_ctx, local_name, mac);424 }425 ScopeDef::ModuleDef(Function(func)) => {426 let ctx = ctx.import_to_add(import_to_add);427 return render_fn(ctx, path_ctx, Some(local_name), func);428 }429 ScopeDef::ModuleDef(EnumVariant(var)) => {430 let ctx = ctx.clone().import_to_add(import_to_add.clone());431 if let Some(item) =432 render_variant_lit(ctx, path_ctx, Some(local_name.clone()), var, None)433 {434 return item;435 }436 }437 _ => (),438 }439440 let completion = ctx.completion;441 let module = completion.module;442 let cap = ctx.snippet_cap();443 let db = completion.db;444 let config = completion.config;445 let requires_import = import_to_add.is_some();446447 let name = local_name.display(db, completion.edition).to_smolstr();448 let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution);449 let mut insert_text = name.clone();450451 // Add `<>` for generic types452 let type_path_no_ty_args = matches!(453 path_ctx,454 PathCompletionCtx { kind: PathKind::Type { .. }, has_type_args: false, .. }455 ) && config.callable.is_some();456 if type_path_no_ty_args && let Some(cap) = cap {457 let has_non_default_type_params = match resolution {458 ScopeDef::ModuleDef(hir::ModuleDef::Adt(it)) => it.has_non_default_type_params(db),459 ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(it)) => {460 it.has_non_default_type_params(db)461 }462 _ => false,463 };464465 if has_non_default_type_params {466 cov_mark::hit!(inserts_angle_brackets_for_generics);467 insert_text = format_smolstr!("{insert_text}<$0>");468 item.lookup_by(name.clone())469 .label(SmolStr::from_iter([&name, "<…>"]))470 .trigger_call_info()471 .insert_snippet(cap, ""); // set is snippet472 }473 }474 let allow_module_path = matches!(path_ctx.kind, PathKind::Use)475 || completion.token.next_token().is_some_and(|it| it.kind() == syntax::T![::])476 || !config.add_colons_to_module;477 if !allow_module_path && matches!(resolution, ScopeDef::ModuleDef(Module(_))) {478 insert_text = format_smolstr!("{insert_text}::");479 item.lookup_by(name.clone()).label(insert_text.clone());480 }481 adds_ret_type_arrow(completion, path_ctx, &mut item, insert_text.into());482483 let mut set_item_relevance = |ty: Type<'db>| {484 if !ty.is_unknown() {485 item.detail(ty.display(db, krate).to_string());486 }487488 let ty = completion.rebase_ty(&ty);489 item.set_relevance(CompletionRelevance {490 type_match: compute_type_match(completion, &ty),491 exact_name_match: compute_exact_name_match(completion, &name),492 is_local: matches!(resolution, ScopeDef::Local(_)),493 requires_import,494 has_local_inherent_impl: compute_has_local_inherent_impl(db, path_ctx, &ty, module),495 ..CompletionRelevance::default()496 });497498 match resolution {499 ScopeDef::Local(_)500 | ScopeDef::ModuleDef(ModuleDef::Const(_) | ModuleDef::Static(_)) => {501 path_ref_match(completion, path_ctx, &ty, &mut item)502 }503 _ => (),504 }505 };506507 match resolution {508 ScopeDef::Local(local) => set_item_relevance(local.ty(db)),509 ScopeDef::ModuleDef(ModuleDef::Adt(adt)) | ScopeDef::AdtSelfType(adt) => {510 set_item_relevance(adt.ty(db))511 }512 // Filtered out above513 ScopeDef::ModuleDef(514 ModuleDef::Function(_) | ModuleDef::EnumVariant(_) | ModuleDef::Macro(_),515 ) => (),516 ScopeDef::ModuleDef(ModuleDef::Const(konst)) => set_item_relevance(konst.ty(db)),517 ScopeDef::ModuleDef(ModuleDef::Static(stat)) => set_item_relevance(stat.ty(db)),518 ScopeDef::ModuleDef(ModuleDef::BuiltinType(bt)) => set_item_relevance(bt.ty(db)),519 ScopeDef::ModuleDef(ModuleDef::TypeAlias(ta)) => set_item_relevance(ta.ty(db)),520 ScopeDef::ImplSelfType(imp) => set_item_relevance(imp.self_ty(db)),521 ScopeDef::GenericParam(_)522 | ScopeDef::Label(_)523 | ScopeDef::Unknown524 | ScopeDef::ModuleDef(ModuleDef::Trait(_) | ModuleDef::Module(_)) => (),525 };526527 item528}529530fn render_resolution_simple_<'db>(531 ctx: RenderContext<'_, 'db>,532 local_name: &hir::Name,533 import_to_add: Option<LocatedImport>,534 resolution: ScopeDef<'db>,535) -> Builder {536 let _p = tracing::info_span!("render_resolution_simple_").entered();537538 let db = ctx.db();539 let ctx = ctx.import_to_add(import_to_add);540 let kind = res_to_kind(resolution);541542 let mut item = CompletionItem::new(543 kind,544 ctx.source_range(),545 local_name.as_str().to_smolstr(),546 ctx.completion.edition,547 );548 if let ScopeDef::ModuleDef(ModuleDef::Const(konst)) = resolution {549 item.const_value(Some(konst), db, ctx.completion.display_target);550 }551 item.set_relevance(ctx.completion_relevance())552 .set_documentation(scope_def_docs(db, resolution))553 .set_deprecated(scope_def_is_deprecated(&ctx, resolution));554555 if let Some(import_to_add) = ctx.import_to_add {556 item.add_import(import_to_add);557 }558559 item.doc_aliases(ctx.doc_aliases);560 item561}562563fn res_to_kind(resolution: ScopeDef<'_>) -> CompletionItemKind {564 use hir::ModuleDef::*;565 match resolution {566 ScopeDef::Unknown => CompletionItemKind::UnresolvedReference,567 ScopeDef::ModuleDef(Function(_)) => CompletionItemKind::SymbolKind(SymbolKind::Function),568 ScopeDef::ModuleDef(EnumVariant(_)) => CompletionItemKind::SymbolKind(SymbolKind::Variant),569 ScopeDef::ModuleDef(Macro(_)) => CompletionItemKind::SymbolKind(SymbolKind::Macro),570 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module),571 ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt {572 hir::Adt::Struct(_) => SymbolKind::Struct,573 hir::Adt::Union(_) => SymbolKind::Union,574 hir::Adt::Enum(_) => SymbolKind::Enum,575 }),576 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const),577 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static),578 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait),579 ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias),580 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,581 ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param {582 hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam,583 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam,584 hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam,585 }),586 ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),587 ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label),588 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => {589 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)590 }591 }592}593594fn scope_def_docs<'db>(595 db: &'db RootDatabase,596 resolution: ScopeDef<'db>,597) -> Option<Documentation<'db>> {598 use hir::ModuleDef::*;599 match resolution {600 ScopeDef::ModuleDef(Module(it)) => it.docs(db),601 ScopeDef::ModuleDef(Adt(it)) => it.docs(db),602 ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(db),603 ScopeDef::ModuleDef(Const(it)) => it.docs(db),604 ScopeDef::ModuleDef(Static(it)) => it.docs(db),605 ScopeDef::ModuleDef(Trait(it)) => it.docs(db),606 ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(db),607 _ => None,608 }609}610611fn scope_def_is_deprecated(ctx: &RenderContext<'_, '_>, resolution: ScopeDef<'_>) -> bool {612 let db = ctx.db();613 match resolution {614 ScopeDef::ModuleDef(hir::ModuleDef::EnumVariant(it)) => ctx.is_variant_deprecated(it),615 ScopeDef::ModuleDef(it) => ctx.is_deprecated(it, it.as_assoc_item(db)),616 ScopeDef::GenericParam(it) => {617 ctx.is_deprecated(it, None /* generic params can't be assoc items */)618 }619 ScopeDef::AdtSelfType(it) => {620 ctx.is_deprecated(it, None /* `Self` can't be an assoc item */)621 }622 _ => false,623 }624}625626pub(crate) fn render_type_keyword_snippet(627 ctx: &CompletionContext<'_, '_>,628 path_ctx: &PathCompletionCtx<'_>,629 label: &str,630 snippet: &str,631) -> Builder {632 let source_range = ctx.source_range();633 let mut item =634 CompletionItem::new(CompletionItemKind::Keyword, source_range, label, ctx.edition);635636 let insert_text = if !snippet.contains('$') {637 item.insert_text(snippet);638 snippet639 } else if let Some(cap) = ctx.config.snippet_cap {640 item.insert_snippet(cap, snippet);641 snippet642 } else {643 label644 };645646 adds_ret_type_arrow(ctx, path_ctx, &mut item, insert_text.to_owned());647 item648}649650fn adds_ret_type_arrow(651 ctx: &CompletionContext<'_, '_>,652 path_ctx: &PathCompletionCtx<'_>,653 item: &mut Builder,654 insert_text: String,655) {656 if let Some((arrow, at)) = path_ctx.required_thin_arrow(&ctx.sema) {657 let mut edit = TextEdit::builder();658659 edit.insert(at, arrow.to_owned());660 edit.replace(ctx.source_range(), insert_text);661662 item.text_edit(edit.finish()).adds_text(SmolStr::new_static(arrow));663 } else {664 item.insert_text(insert_text);665 }666}667668// FIXME: This checks types without possible coercions which some completions might want to do669fn match_types(670 ctx: &CompletionContext<'_, '_>,671 ty1: &hir::Type<'_>,672 ty2: &hir::Type<'_>,673) -> Option<CompletionRelevanceTypeMatch> {674 if ty1 == ty2 {675 Some(CompletionRelevanceTypeMatch::Exact)676 } else if ty1.could_unify_with(ctx.db, ty2) {677 Some(CompletionRelevanceTypeMatch::CouldUnify)678 } else {679 None680 }681}682683fn compute_type_match(684 ctx: &CompletionContext<'_, '_>,685 completion_ty: &hir::Type<'_>,686) -> Option<CompletionRelevanceTypeMatch> {687 let expected_type = ctx.expected_type.as_ref()?;688689 // We don't ever consider unit type to be an exact type match, since690 // nearly always this is not meaningful to the user.691 if expected_type.is_unit() || completion_ty.is_unit() {692 return None;693 }694 if expected_type.is_unknown() || completion_ty.is_unknown() {695 return None;696 }697698 // &mut ty -> &ty699 if completion_ty.is_mutable_reference()700 && let Some((expected_type, _)) = expected_type.as_reference()701 && let Some((completion_ty, _)) = completion_ty.as_reference()702 {703 return match_types(ctx, &expected_type, &completion_ty);704 }705706 match_types(ctx, expected_type, completion_ty)707}708709fn compute_has_local_inherent_impl(710 db: &RootDatabase,711 path_ctx: &PathCompletionCtx<'_>,712 completion_ty: &hir::Type<'_>,713 curr_module: hir::Module,714) -> bool {715 matches!(path_ctx.kind, PathKind::Type { location: TypeLocation::ImplTarget })716 && Impl::all_for_type(db, completion_ty.clone())717 .iter()718 .any(|imp| imp.trait_(db).is_none() && imp.module(db) == curr_module)719}720721fn compute_exact_name_match(ctx: &CompletionContext<'_, '_>, completion_name: &str) -> bool {722 ctx.expected_name.as_ref().is_some_and(|name| {723 let name = name.text().strip_prefix("r#").unwrap_or(name.text());724 let completion_name = completion_name.strip_prefix("r#").unwrap_or(completion_name);725 name.trim_matches('_') == completion_name.trim_matches('_')726 })727}728729fn compute_ref_match(730 ctx: &CompletionContext<'_, '_>,731 completion_ty: &hir::Type<'_>,732) -> Option<CompletionItemRefMode> {733 if compute_type_match(ctx, completion_ty).is_some() || completion_ty.is_unit() {734 return None;735 }736 let expected_type = ctx.expected_type.as_ref()?;737 let expected_without_ref = expected_type.as_reference();738 let completion_without_ref = completion_ty.as_reference();739740 if let Some((expected_without_ref, _)) = &expected_without_ref741 && (completion_without_ref.is_none()742 || completion_ty.could_unify_with(ctx.db, expected_without_ref))743 && completion_ty744 .autoderef(ctx.db)745 .any(|ty| !ty.is_unknown() && ty.could_unify_with(ctx.db, expected_without_ref))746 {747 cov_mark::hit!(suggest_ref);748 let mutability = if expected_type.is_mutable_reference() {749 hir::Mutability::Mut750 } else {751 hir::Mutability::Shared752 };753 return Some(CompletionItemRefMode::Reference(mutability));754 }755756 if let Some((completion_without_ref, _)) = completion_without_ref757 && completion_without_ref == *expected_type758 && completion_without_ref.is_copy(ctx.db)759 {760 cov_mark::hit!(suggest_deref);761 return Some(CompletionItemRefMode::Dereference);762 }763764 None765}766767fn path_ref_match(768 completion: &CompletionContext<'_, '_>,769 path_ctx: &PathCompletionCtx<'_>,770 ty: &hir::Type<'_>,771 item: &mut Builder,772) {773 if let Some(original_path) = &path_ctx.original_path {774 // At least one char was typed by the user already, in that case look for the original path775 if let Some(original_path) = completion.sema.original_range_opt(original_path.syntax())776 && let Some(ref_mode) = compute_ref_match(completion, ty)777 {778 item.ref_match(ref_mode, original_path.range.start());779 }780 } else {781 // completion requested on an empty identifier, there is no path here yet.782 // FIXME: This might create inconsistent completions where we show a ref match in macro inputs783 // as long as nothing was typed yet784 if let Some(ref_mode) = compute_ref_match(completion, ty) {785 item.ref_match(ref_mode, completion.source_range().start());786 }787 }788}789790#[cfg(test)]791mod tests {792 use std::cmp;793794 use expect_test::{Expect, expect};795 use ide_db::SymbolKind;796 use itertools::Itertools;797798 use crate::{799 CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch,800 item::CompletionRelevanceTypeMatch,801 tests::{TEST_CONFIG, check_edit, do_completion, get_all_items},802 };803804 #[track_caller]805 fn check(806 #[rust_analyzer::rust_fixture] ra_fixture: &str,807 kind: impl Into<CompletionItemKind>,808 expect: Expect,809 ) {810 let actual = do_completion(ra_fixture, kind.into());811 expect.assert_debug_eq(&actual);812 }813814 #[track_caller]815 fn check_kinds(816 #[rust_analyzer::rust_fixture] ra_fixture: &str,817 kinds: &[CompletionItemKind],818 expect: Expect,819 ) {820 let actual: Vec<_> =821 kinds.iter().flat_map(|&kind| do_completion(ra_fixture, kind)).collect();822 expect.assert_debug_eq(&actual);823 }824825 #[track_caller]826 fn check_function_relevance(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {827 let actual: Vec<_> =828 do_completion(ra_fixture, CompletionItemKind::SymbolKind(SymbolKind::Method))829 .into_iter()830 .map(|item| (item.detail.unwrap_or_default(), item.relevance.function))831 .collect();832833 expect.assert_debug_eq(&actual);834 }835836 #[track_caller]837 fn check_relevance_for_kinds(838 #[rust_analyzer::rust_fixture] ra_fixture: &str,839 kinds: &[CompletionItemKind],840 expect: Expect,841 ) {842 let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);843 actual.retain(|it| kinds.contains(&it.kind));844 actual.sort_by_key(|it| (cmp::Reverse(it.relevance.score()), it.label.primary.clone()));845 check_relevance_(actual, expect);846 }847848 #[track_caller]849 fn check_relevance(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {850 let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);851 actual.retain(|it| it.kind != CompletionItemKind::Snippet);852 actual.retain(|it| it.kind != CompletionItemKind::Keyword);853 actual.retain(|it| it.kind != CompletionItemKind::BuiltinType);854 actual.sort_by_key(|it| (cmp::Reverse(it.relevance.score()), it.label.primary.clone()));855 check_relevance_(actual, expect);856 }857858 #[track_caller]859 fn check_relevance_(actual: Vec<CompletionItem>, expect: Expect) {860 let actual = actual861 .into_iter()862 .flat_map(|it| {863 let mut items = vec![];864865 let tag = it.kind.tag();866 let relevance = display_relevance(it.relevance);867 items.push(format!(868 "{tag} {} {} {relevance}\n",869 it.label.primary,870 it.label.detail_right.as_deref().unwrap_or_default(),871 ));872873 if let Some((label, _indel, relevance)) = it.ref_match() {874 let relevance = display_relevance(relevance);875876 items.push(format!("{tag} {label} {relevance}\n"));877 }878879 items880 })881 .collect::<String>();882883 expect.assert_eq(&actual);884885 fn display_relevance(relevance: CompletionRelevance) -> String {886 let CompletionRelevance {887 exact_name_match,888 type_match,889 is_local,890 is_missing,891 trait_,892 is_name_already_imported: _,893 requires_import,894 is_private_editable: _,895 postfix_match,896 function: _,897 is_skipping_completion: _,898 has_local_inherent_impl,899 is_deprecated,900 } = relevance;901 let relevance_factors = [902 (type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"),903 (type_match == Some(CompletionRelevanceTypeMatch::CouldUnify), "type_could_unify"),904 (exact_name_match, "name"),905 (is_local, "local"),906 (is_missing, "missing"),907 (postfix_match == Some(CompletionRelevancePostfixMatch::Exact), "snippet"),908 (trait_.is_some_and(|it| it.is_op_method), "op_method"),909 (requires_import, "requires_import"),910 (has_local_inherent_impl, "has_local_inherent_impl"),911 (is_deprecated, "deprecated"),912 ]913 .into_iter()914 .filter_map(|(cond, desc)| cond.then_some(desc))915 .join("+");916917 format!("[{relevance_factors}]")918 }919 }920921 #[test]922 fn trait_imported_as_underscore_should_not_appear_auto_import_again() {923 // make sure there has no `requires_import`924 // see https://github.com/rust-lang/rust-analyzer/issues/19767925 check_relevance(926 r#"927//- /dep.rs crate:dep928pub trait MyTrait {929 fn my_method(&self);930}931932//- /main.rs crate:main deps:dep933use dep::MyTrait as _;934struct MyStruct;935impl dep::MyTrait for MyStruct {936 fn my_method(&self) {}937}938fn main() {939 MyStruct::my_method$0940}941"#,942 expect![[r#"943 me my_method(…) fn(&self) []944 "#]],945 );946 }947948 #[test]949 fn set_struct_type_completion_info() {950 check_relevance(951 r#"952//- /lib.rs crate:dep953954pub mod test_mod_b {955 pub struct Struct {}956}957958pub mod test_mod_a {959 pub struct Struct {}960}961962//- /main.rs crate:main deps:dep963964fn test(input: dep::test_mod_b::Struct) { }965966fn main() {967 test(Struct$0);968}969"#,970 expect![[r#"971 st dep::test_mod_b::Struct {…} dep::test_mod_b::Struct { } [type]972 ex dep::test_mod_b::Struct { } [type]973 st Struct Struct [type+requires_import]974 md dep:: []975 fn main() fn() []976 fn test(…) fn(Struct) []977 st Struct Struct [requires_import]978 "#]],979 );980 }981982 #[test]983 fn set_union_type_completion_info() {984 check_relevance(985 r#"986//- /lib.rs crate:dep987988pub mod test_mod_b {989 pub union Union {990 a: i32,991 b: i32992 }993}994995pub mod test_mod_a {996 pub enum Union {997 a: i32,998 b: i32999 }1000}10011002//- /main.rs crate:main deps:dep10031004fn test(input: dep::test_mod_b::Union) { }10051006fn main() {1007 test(Union$0);1008}1009"#,1010 expect![[r#"1011 un Union Union [type+requires_import]1012 md dep:: []1013 fn main() fn() []1014 fn test(…) fn(Union) []1015 en Union Union [requires_import]1016 "#]],1017 );1018 }10191020 #[test]1021 fn set_enum_type_completion_info() {1022 check_relevance(1023 r#"1024//- /lib.rs crate:dep10251026pub mod test_mod_b {1027 pub enum Enum {1028 variant1029 }1030}10311032pub mod test_mod_a {1033 pub enum Enum {1034 variant1035 }1036}10371038//- /main.rs crate:main deps:dep10391040fn test(input: dep::test_mod_b::Enum) { }10411042fn main() {1043 test(Enum$0);1044}1045"#,1046 expect![[r#"1047 ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type]1048 ex dep::test_mod_b::Enum::variant [type]1049 en Enum Enum [type+requires_import]1050 md dep:: []1051 fn main() fn() []1052 fn test(…) fn(Enum) []1053 en Enum Enum [requires_import]1054 "#]],1055 );1056 }10571058 #[test]1059 fn set_enum_variant_type_completion_info() {1060 check_relevance(1061 r#"1062//- /lib.rs crate:dep10631064pub mod test_mod_b {1065 pub enum Enum {1066 Variant1067 }1068}10691070pub mod test_mod_a {1071 pub enum Enum {1072 Variant1073 }1074}10751076//- /main.rs crate:main deps:dep10771078fn test(input: dep::test_mod_b::Enum) { }10791080fn main() {1081 test(Variant$0);1082}1083"#,1084 expect![[r#"1085 ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type]1086 ex dep::test_mod_b::Enum::Variant [type]1087 ev Variant Variant [type+requires_import]1088 md dep:: []1089 fn main() fn() []1090 fn test(…) fn(Enum) []1091 ev Variant Variant [requires_import]1092 "#]],1093 );1094 }10951096 #[test]1097 fn set_fn_type_completion_info() {1098 check_relevance(1099 r#"1100//- /lib.rs crate:dep11011102pub mod test_mod_b {1103 pub fn function(j: isize) -> i32 {}1104}11051106pub mod test_mod_a {1107 pub fn function(i: usize) -> i32 {}1108}11091110//- /main.rs crate:main deps:dep11111112fn test(input: fn(usize) -> i32) { }11131114fn main() {1115 test(function$0);1116}1117"#,1118 expect![[r#"1119 md dep:: []1120 fn main() fn() []1121 fn test(…) fn(fn(usize) -> i32) []1122 fn function fn(usize) -> i32 [requires_import]1123 fn function(…) fn(isize) -> i32 [requires_import]1124 "#]],1125 );1126 }11271128 #[test]1129 fn set_const_type_completion_info() {1130 check_relevance(1131 r#"1132//- /lib.rs crate:dep11331134pub mod test_mod_b {1135 pub const CONST: i32 = 1;1136}11371138pub mod test_mod_a {1139 pub const CONST: i64 = 2;1140}11411142//- /main.rs crate:main deps:dep11431144fn test(input: i32) { }11451146fn main() {1147 test(CONST$0);1148}1149"#,1150 expect![[r#"1151 ct CONST i32 [type+requires_import]1152 md dep:: []1153 fn main() fn() []1154 fn test(…) fn(i32) []1155 ct CONST i64 [requires_import]1156 "#]],1157 );1158 }11591160 #[test]1161 fn set_static_type_completion_info() {1162 check_relevance(1163 r#"1164//- /lib.rs crate:dep11651166pub mod test_mod_b {1167 pub static STATIC: i32 = 5;1168}11691170pub mod test_mod_a {1171 pub static STATIC: i64 = 5;1172}11731174//- /main.rs crate:main deps:dep11751176fn test(input: i32) { }11771178fn main() {1179 test(STATIC$0);1180}1181"#,1182 expect![[r#"1183 sc STATIC i32 [type+requires_import]1184 md dep:: []1185 fn main() fn() []1186 fn test(…) fn(i32) []1187 sc STATIC i64 [requires_import]1188 "#]],1189 );1190 }11911192 #[test]1193 fn set_self_type_completion_info_with_params() {1194 check_relevance(1195 r#"1196//- /lib.rs crate:dep1197pub struct Struct;11981199impl Struct {1200 pub fn Function(&self, input: i32) -> bool {1201 false1202 }1203}120412051206//- /main.rs crate:main deps:dep12071208use dep::Struct;120912101211fn test(input: fn(&dep::Struct, i32) -> bool) { }12121213fn main() {1214 test(Struct::Function$0);1215}12161217"#,1218 expect![[r#"1219 me Function fn(&self, i32) -> bool []1220 "#]],1221 );1222 }12231224 #[test]1225 fn set_self_type_completion_info() {1226 check_relevance(1227 r#"1228//- /main.rs crate:main12291230struct Struct;12311232impl Struct {1233fn test(&self) {1234 func(Self$0);1235 }1236}12371238fn func(input: Struct) { }12391240"#,1241 expect![[r#"1242 st Self Self [type]1243 st Struct Struct [type]1244 sp Self Struct [type]1245 st Struct Struct [type]1246 ex Struct [type]1247 lc self &Struct [local]1248 fn func(…) fn(Struct) []1249 me self.test() fn(&self) []1250 "#]],1251 );1252 }12531254 #[test]1255 fn set_type_alias_completion_info() {1256 check_relevance(1257 r#"1258//- /main.rs crate:main1259struct Struct;1260type Alias = Struct;12611262impl Struct {1263 fn test(&self) {1264 func(Self$0);1265 }1266}1267fn func(input: Struct) { }1268"#,1269 expect![[r#"1270 st Self Self [type]1271 st Struct Struct [type]1272 ta Alias Struct [type]1273 sp Self Struct [type]1274 st Struct Struct [type]1275 ex Struct [type]1276 lc self &Struct [local]1277 fn func(…) fn(Struct) []1278 me self.test() fn(&self) []1279 "#]],1280 );1281 }12821283 #[test]1284 fn set_builtin_type_completion_info() {1285 check_relevance(1286 r#"1287//- /main.rs crate:main12881289fn test(input: bool) { }1290 pub Input: bool = false;12911292fn main() {1293 let input = false;1294 let inputbad = 3;1295 test(inp$0);1296}1297"#,1298 expect![[r#"1299 lc input bool [type+name+local]1300 ex false [type]1301 ex input [type]1302 ex true [type]1303 lc inputbad i32 [local]1304 fn main() fn() []1305 fn test(…) fn(bool) []1306 "#]],1307 );1308 }13091310 #[test]1311 fn enum_detail_includes_record_fields() {1312 check(1313 r#"1314enum Foo { Foo { x: i32, y: i32 } }13151316fn main() { Foo::Fo$0 }1317"#,1318 SymbolKind::Variant,1319 expect![[r#"1320 [1321 CompletionItem {1322 label: "Foo {…}",1323 detail_left: None,1324 detail_right: Some(1325 "Foo { x: i32, y: i32 }",1326 ),1327 source_range: 54..56,1328 delete: 54..56,1329 insert: "Foo { x: ${1:()}, y: ${2:()} }$0",1330 kind: SymbolKind(1331 Variant,1332 ),1333 lookup: "Foo{}",1334 detail: "Foo { x: i32, y: i32 }",1335 relevance: CompletionRelevance {1336 exact_name_match: false,1337 type_match: None,1338 is_local: false,1339 is_missing: false,1340 trait_: None,1341 is_name_already_imported: false,1342 requires_import: false,1343 is_private_editable: false,1344 postfix_match: None,1345 function: Some(1346 CompletionRelevanceFn {1347 has_params: true,1348 has_self_param: false,1349 return_type: DirectConstructor,1350 },1351 ),1352 is_skipping_completion: false,1353 has_local_inherent_impl: false,1354 is_deprecated: false,1355 },1356 trigger_call_info: true,1357 },1358 ]1359 "#]],1360 );1361 }13621363 #[test]1364 fn enum_detail_includes_tuple_fields() {1365 check(1366 r#"1367enum Foo { Foo (i32, i32) }13681369fn main() { Foo::Fo$0 }1370"#,1371 SymbolKind::Variant,1372 expect![[r#"1373 [1374 CompletionItem {1375 label: "Foo(…)",1376 detail_left: None,1377 detail_right: Some(1378 "Foo(i32, i32)",1379 ),1380 source_range: 46..48,1381 delete: 46..48,1382 insert: "Foo(${1:()}, ${2:()})$0",1383 kind: SymbolKind(1384 Variant,1385 ),1386 lookup: "Foo()",1387 detail: "Foo(i32, i32)",1388 relevance: CompletionRelevance {1389 exact_name_match: false,1390 type_match: None,1391 is_local: false,1392 is_missing: false,1393 trait_: None,1394 is_name_already_imported: false,1395 requires_import: false,1396 is_private_editable: false,1397 postfix_match: None,1398 function: Some(1399 CompletionRelevanceFn {1400 has_params: true,1401 has_self_param: false,1402 return_type: DirectConstructor,1403 },1404 ),1405 is_skipping_completion: false,1406 has_local_inherent_impl: false,1407 is_deprecated: false,1408 },1409 trigger_call_info: true,1410 },1411 ]1412 "#]],1413 );1414 }14151416 #[test]1417 fn fn_detail_includes_args_and_return_type() {1418 check(1419 r#"1420fn foo<T>(a: u32, b: u32, t: T) -> (u32, T) { (a, t) }14211422fn main() { fo$0 }1423"#,1424 SymbolKind::Function,1425 expect![[r#"1426 [1427 CompletionItem {1428 label: "foo(…)",1429 detail_left: None,1430 detail_right: Some(1431 "fn(u32, u32, T) -> (u32, T)",1432 ),1433 source_range: 68..70,1434 delete: 68..70,1435 insert: "foo(${1:a}, ${2:b}, ${3:t})$0",1436 kind: SymbolKind(1437 Function,1438 ),1439 lookup: "foo",1440 detail: "fn(u32, u32, T) -> (u32, T)",1441 trigger_call_info: true,1442 },1443 CompletionItem {1444 label: "main()",1445 detail_left: None,1446 detail_right: Some(1447 "fn()",1448 ),1449 source_range: 68..70,1450 delete: 68..70,1451 insert: "main();$0",1452 kind: SymbolKind(1453 Function,1454 ),1455 lookup: "main",1456 detail: "fn()",1457 },1458 ]1459 "#]],1460 );1461 }14621463 #[test]1464 fn fn_detail_includes_variadics() {1465 check(1466 r#"1467unsafe extern "C" fn foo(a: u32, b: u32, ...) {}14681469fn main() { fo$0 }1470"#,1471 SymbolKind::Function,1472 expect![[r#"1473 [1474 CompletionItem {1475 label: "foo(…)",1476 detail_left: None,1477 detail_right: Some(1478 "unsafe fn(u32, u32, ...)",1479 ),1480 source_range: 62..64,1481 delete: 62..64,1482 insert: "foo(${1:a}, ${2:b});$0",1483 kind: SymbolKind(1484 Function,1485 ),1486 lookup: "foo",1487 detail: "unsafe fn(u32, u32, ...)",1488 trigger_call_info: true,1489 },1490 CompletionItem {1491 label: "main()",1492 detail_left: None,1493 detail_right: Some(1494 "fn()",1495 ),1496 source_range: 62..64,1497 delete: 62..64,1498 insert: "main();$0",1499 kind: SymbolKind(1500 Function,1501 ),1502 lookup: "main",1503 detail: "fn()",1504 },1505 ]1506 "#]],1507 );1508 }15091510 #[test]1511 fn enum_detail_just_name_for_unit() {1512 check(1513 r#"1514enum Foo { Foo }15151516fn main() { Foo::Fo$0 }1517"#,1518 SymbolKind::Variant,1519 expect![[r#"1520 [1521 CompletionItem {1522 label: "Foo",1523 detail_left: None,1524 detail_right: Some(1525 "Foo",1526 ),1527 source_range: 35..37,1528 delete: 35..37,1529 insert: "Foo$0",1530 kind: SymbolKind(1531 Variant,1532 ),1533 detail: "Foo",1534 relevance: CompletionRelevance {1535 exact_name_match: false,1536 type_match: None,1537 is_local: false,1538 is_missing: false,1539 trait_: None,1540 is_name_already_imported: false,1541 requires_import: false,1542 is_private_editable: false,1543 postfix_match: None,1544 function: Some(1545 CompletionRelevanceFn {1546 has_params: false,1547 has_self_param: false,1548 return_type: DirectConstructor,1549 },1550 ),1551 is_skipping_completion: false,1552 has_local_inherent_impl: false,1553 is_deprecated: false,1554 },1555 trigger_call_info: true,1556 },1557 ]1558 "#]],1559 );1560 }15611562 #[test]1563 fn lookup_enums_by_two_qualifiers() {1564 check_kinds(1565 r#"1566mod m {1567 pub enum Spam { Foo, Bar(i32) }1568}1569fn main() { let _: m::Spam = S$0 }1570"#,1571 &[1572 CompletionItemKind::SymbolKind(SymbolKind::Function),1573 CompletionItemKind::SymbolKind(SymbolKind::Module),1574 CompletionItemKind::SymbolKind(SymbolKind::Variant),1575 ],1576 expect![[r#"1577 [1578 CompletionItem {1579 label: "main()",1580 detail_left: None,1581 detail_right: Some(1582 "fn()",1583 ),1584 source_range: 75..76,1585 delete: 75..76,1586 insert: "main();$0",1587 kind: SymbolKind(1588 Function,1589 ),1590 lookup: "main",1591 detail: "fn()",1592 },1593 CompletionItem {1594 label: "m::",1595 detail_left: None,1596 detail_right: None,1597 source_range: 75..76,1598 delete: 75..76,1599 insert: "m::",1600 kind: SymbolKind(1601 Module,1602 ),1603 lookup: "m",1604 },1605 CompletionItem {1606 label: "m::Spam::Bar(…)",1607 detail_left: None,1608 detail_right: Some(1609 "m::Spam::Bar(i32)",1610 ),1611 source_range: 75..76,1612 delete: 75..76,1613 insert: "m::Spam::Bar(${1:()})$0",1614 kind: SymbolKind(1615 Variant,1616 ),1617 lookup: "Spam::Bar()",1618 detail: "m::Spam::Bar(i32)",1619 relevance: CompletionRelevance {1620 exact_name_match: false,1621 type_match: Some(1622 Exact,1623 ),1624 is_local: false,1625 is_missing: false,1626 trait_: None,1627 is_name_already_imported: false,1628 requires_import: false,1629 is_private_editable: false,1630 postfix_match: None,1631 function: Some(1632 CompletionRelevanceFn {1633 has_params: true,1634 has_self_param: false,1635 return_type: DirectConstructor,1636 },1637 ),1638 is_skipping_completion: false,1639 has_local_inherent_impl: false,1640 is_deprecated: false,1641 },1642 trigger_call_info: true,1643 },1644 CompletionItem {1645 label: "m::Spam::Foo",1646 detail_left: None,1647 detail_right: Some(1648 "m::Spam::Foo",1649 ),1650 source_range: 75..76,1651 delete: 75..76,1652 insert: "m::Spam::Foo$0",1653 kind: SymbolKind(1654 Variant,1655 ),1656 lookup: "Spam::Foo",1657 detail: "m::Spam::Foo",1658 relevance: CompletionRelevance {1659 exact_name_match: false,1660 type_match: Some(1661 Exact,1662 ),1663 is_local: false,1664 is_missing: false,1665 trait_: None,1666 is_name_already_imported: false,1667 requires_import: false,1668 is_private_editable: false,1669 postfix_match: None,1670 function: Some(1671 CompletionRelevanceFn {1672 has_params: false,1673 has_self_param: false,1674 return_type: DirectConstructor,1675 },1676 ),1677 is_skipping_completion: false,1678 has_local_inherent_impl: false,1679 is_deprecated: false,1680 },1681 trigger_call_info: true,1682 },1683 ]1684 "#]],1685 )1686 }16871688 #[test]1689 fn sets_deprecated_flag_in_items() {1690 check(1691 r#"1692#[deprecated]1693mod something_deprecated {}16941695fn main() { som$0 }1696"#,1697 SymbolKind::Module,1698 expect![[r#"1699 [1700 CompletionItem {1701 label: "something_deprecated::",1702 detail_left: None,1703 detail_right: None,1704 source_range: 55..58,1705 delete: 55..58,1706 insert: "something_deprecated::",1707 kind: SymbolKind(1708 Module,1709 ),1710 lookup: "something_deprecated",1711 deprecated: true,1712 relevance: CompletionRelevance {1713 exact_name_match: false,1714 type_match: None,1715 is_local: false,1716 is_missing: false,1717 trait_: None,1718 is_name_already_imported: false,1719 requires_import: false,1720 is_private_editable: false,1721 postfix_match: None,1722 function: None,1723 is_skipping_completion: false,1724 has_local_inherent_impl: false,1725 is_deprecated: true,1726 },1727 },1728 ]1729 "#]],1730 );17311732 check(1733 r#"1734#[deprecated]1735fn something_deprecated() {}17361737fn main() { som$0 }1738"#,1739 SymbolKind::Function,1740 expect![[r#"1741 [1742 CompletionItem {1743 label: "main()",1744 detail_left: None,1745 detail_right: Some(1746 "fn()",1747 ),1748 source_range: 56..59,1749 delete: 56..59,1750 insert: "main();$0",1751 kind: SymbolKind(1752 Function,1753 ),1754 lookup: "main",1755 detail: "fn()",1756 },1757 CompletionItem {1758 label: "something_deprecated()",1759 detail_left: None,1760 detail_right: Some(1761 "fn()",1762 ),1763 source_range: 56..59,1764 delete: 56..59,1765 insert: "something_deprecated();$0",1766 kind: SymbolKind(1767 Function,1768 ),1769 lookup: "something_deprecated",1770 detail: "fn()",1771 deprecated: true,1772 relevance: CompletionRelevance {1773 exact_name_match: false,1774 type_match: None,1775 is_local: false,1776 is_missing: false,1777 trait_: None,1778 is_name_already_imported: false,1779 requires_import: false,1780 is_private_editable: false,1781 postfix_match: None,1782 function: None,1783 is_skipping_completion: false,1784 has_local_inherent_impl: false,1785 is_deprecated: true,1786 },1787 },1788 ]1789 "#]],1790 );17911792 check(1793 r#"1794#[deprecated]1795struct A;17961797fn main() { A$0 }1798"#,1799 SymbolKind::Struct,1800 expect![[r#"1801 [1802 CompletionItem {1803 label: "A",1804 detail_left: None,1805 detail_right: Some(1806 "A",1807 ),1808 source_range: 37..38,1809 delete: 37..38,1810 insert: "A",1811 kind: SymbolKind(1812 Struct,1813 ),1814 detail: "A",1815 deprecated: true,1816 relevance: CompletionRelevance {1817 exact_name_match: false,1818 type_match: None,1819 is_local: false,1820 is_missing: false,1821 trait_: None,1822 is_name_already_imported: false,1823 requires_import: false,1824 is_private_editable: false,1825 postfix_match: None,1826 function: None,1827 is_skipping_completion: false,1828 has_local_inherent_impl: false,1829 is_deprecated: true,1830 },1831 },1832 ]1833 "#]],1834 );18351836 check(1837 r#"1838#[deprecated]1839enum A {}18401841fn main() { A$0 }1842"#,1843 SymbolKind::Enum,1844 expect![[r#"1845 [1846 CompletionItem {1847 label: "A",1848 detail_left: None,1849 detail_right: Some(1850 "A",1851 ),1852 source_range: 37..38,1853 delete: 37..38,1854 insert: "A",1855 kind: SymbolKind(1856 Enum,1857 ),1858 detail: "A",1859 deprecated: true,1860 relevance: CompletionRelevance {1861 exact_name_match: false,1862 type_match: None,1863 is_local: false,1864 is_missing: false,1865 trait_: None,1866 is_name_already_imported: false,1867 requires_import: false,1868 is_private_editable: false,1869 postfix_match: None,1870 function: None,1871 is_skipping_completion: false,1872 has_local_inherent_impl: false,1873 is_deprecated: true,1874 },1875 },1876 ]1877 "#]],1878 );18791880 check(1881 r#"1882enum A {1883 Okay,1884 #[deprecated]1885 Old,1886}18871888fn main() { A::$0 }1889"#,1890 SymbolKind::Variant,1891 expect![[r#"1892 [1893 CompletionItem {1894 label: "Okay",1895 detail_left: None,1896 detail_right: Some(1897 "Okay",1898 ),1899 source_range: 64..64,1900 delete: 64..64,1901 insert: "Okay$0",1902 kind: SymbolKind(1903 Variant,1904 ),1905 detail: "Okay",1906 relevance: CompletionRelevance {1907 exact_name_match: false,1908 type_match: None,1909 is_local: false,1910 is_missing: false,1911 trait_: None,1912 is_name_already_imported: false,1913 requires_import: false,1914 is_private_editable: false,1915 postfix_match: None,1916 function: Some(1917 CompletionRelevanceFn {1918 has_params: false,1919 has_self_param: false,1920 return_type: DirectConstructor,1921 },1922 ),1923 is_skipping_completion: false,1924 has_local_inherent_impl: false,1925 is_deprecated: false,1926 },1927 trigger_call_info: true,1928 },1929 CompletionItem {1930 label: "Old",1931 detail_left: None,1932 detail_right: Some(1933 "Old",1934 ),1935 source_range: 64..64,1936 delete: 64..64,1937 insert: "Old$0",1938 kind: SymbolKind(1939 Variant,1940 ),1941 detail: "Old",1942 deprecated: true,1943 relevance: CompletionRelevance {1944 exact_name_match: false,1945 type_match: None,1946 is_local: false,1947 is_missing: false,1948 trait_: None,1949 is_name_already_imported: false,1950 requires_import: false,1951 is_private_editable: false,1952 postfix_match: None,1953 function: Some(1954 CompletionRelevanceFn {1955 has_params: false,1956 has_self_param: false,1957 return_type: DirectConstructor,1958 },1959 ),1960 is_skipping_completion: false,1961 has_local_inherent_impl: false,1962 is_deprecated: true,1963 },1964 trigger_call_info: true,1965 },1966 ]1967 "#]],1968 );19691970 check(1971 r#"1972#[deprecated]1973const A: i32 = 0;19741975fn main() { A$0 }1976"#,1977 SymbolKind::Const,1978 expect![[r#"1979 [1980 CompletionItem {1981 label: "A",1982 detail_left: Some(1983 " = 0",1984 ),1985 detail_right: Some(1986 "i32",1987 ),1988 source_range: 45..46,1989 delete: 45..46,1990 insert: "A",1991 kind: SymbolKind(1992 Const,1993 ),1994 detail: "i32",1995 deprecated: true,1996 relevance: CompletionRelevance {1997 exact_name_match: false,1998 type_match: None,1999 is_local: false,2000 is_missing: false,
Findings
✓ No findings reported for this file.