src/tools/rust-analyzer/crates/ide-completion/src/render.rs RUST 3,962 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 3,962.
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.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.