Repository: rust-lang/rust-analyzer
Pull request #12597 (2022-06-20 17:02:10)

fix: Fix auto-ref completions inserting into wrong locations
Fixes https://github.com/rust-lang/rust-analyzer/issues/8058


Patch:
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index 4f522ee761..147563ef10 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -36,9 +36,9 @@ use crate::{
         const_::render_const,
         function::{render_fn, render_method},
         literal::{render_struct_literal, render_variant_lit},
-        macro_::render_macro,
+        macro_::{render_macro, render_macro_pat},
         pattern::{render_struct_pat, render_variant_pat},
-        render_field, render_path_resolution, render_resolution_simple, render_tuple_field,
+        render_field, render_path_resolution, render_pattern_resolution, render_tuple_field,
         type_alias::{render_type_alias, render_type_alias_with_eq},
         union_literal::render_union_literal,
         RenderContext,
@@ -134,10 +134,14 @@ impl Completions {
         item.add_to(self);
     }
 
-    pub(crate) fn add_crate_roots(&mut self, ctx: &CompletionContext) {
+    pub(crate) fn add_crate_roots(
+        &mut self,
+        ctx: &CompletionContext,
+        path_ctx: &PathCompletionCtx,
+    ) {
         ctx.process_all_names(&mut |name, res| match res {
             ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => {
-                self.add_module(ctx, m, name);
+                self.add_module(ctx, path_ctx, m, name);
             }
             _ => (),
         });
@@ -160,25 +164,36 @@ impl Completions {
         );
     }
 
-    pub(crate) fn add_resolution_simple(
+    pub(crate) fn add_pattern_resolution(
         &mut self,
         ctx: &CompletionContext,
+        pattern_ctx: &PatternContext,
         local_name: hir::Name,
         resolution: hir::ScopeDef,
     ) {
         if ctx.is_scope_def_hidden(resolution) {
+            cov_mark::hit!(qualified_path_doc_hidden);
             return;
         }
-        self.add(render_resolution_simple(RenderContext::new(ctx), local_name, resolution).build());
+        self.add(
+            render_pattern_resolution(RenderContext::new(ctx), pattern_ctx, local_name, resolution)
+                .build(),
+        );
     }
 
     pub(crate) fn add_module(
         &mut self,
         ctx: &CompletionContext,
+        path_ctx: &PathCompletionCtx,
         module: hir::Module,
         local_name: hir::Name,
     ) {
-        self.add_resolution_simple(ctx, local_name, hir::ScopeDef::ModuleDef(module.into()));
+        self.add_path_resolution(
+            ctx,
+            path_ctx,
+            local_name,
+            hir::ScopeDef::ModuleDef(module.into()),
+        );
     }
 
     pub(crate) fn add_macro(
@@ -204,6 +219,29 @@ impl Completions {
         );
     }
 
+    pub(crate) fn add_macro_pat(
+        &mut self,
+        ctx: &CompletionContext,
+        pattern_ctx: &PatternContext,
+        mac: hir::Macro,
+        local_name: hir::Name,
+    ) {
+        let is_private_editable = match ctx.is_visible(&mac) {
+            Visible::Yes => false,
+            Visible::Editable => true,
+            Visible::No => return,
+        };
+        self.add(
+            render_macro_pat(
+                RenderContext::new(ctx).private_editable(is_private_editable),
+                pattern_ctx,
+                local_name,
+                mac,
+            )
+            .build(),
+        );
+    }
+
     pub(crate) fn add_function(
         &mut self,
         ctx: &CompletionContext,
@@ -341,6 +379,7 @@ impl Completions {
     pub(crate) fn add_field(
         &mut self,
         ctx: &CompletionContext,
+        dot_access: &DotAccess,
         receiver: Option<hir::Name>,
         field: hir::Field,
         ty: &hir::Type,
@@ -352,6 +391,7 @@ impl Completions {
         };
         let item = render_field(
             RenderContext::new(ctx).private_editable(is_private_editable),
+            dot_access,
             receiver,
             field,
             ty,
diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs
index 1c4f9a3113..154c096f4d 100644
--- a/crates/ide-completion/src/completions/attribute.rs
+++ b/crates/ide-completion/src/completions/attribute.rs
@@ -95,7 +95,7 @@ pub(crate) fn complete_attribute_path(
                         acc.add_macro(ctx, path_ctx, m, name)
                     }
                     hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
-                        acc.add_module(ctx, m, name)
+                        acc.add_module(ctx, path_ctx, m, name)
                     }
                     _ => (),
                 }
@@ -103,14 +103,16 @@ pub(crate) fn complete_attribute_path(
             return;
         }
         // fresh use tree with leading colon2, only show crate roots
-        Qualified::Absolute => acc.add_crate_roots(ctx),
+        Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
         // only show modules in a fresh UseTree
         Qualified::No => {
             ctx.process_all_names(&mut |name, def| match def {
                 hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => {
                     acc.add_macro(ctx, path_ctx, m, name)
                 }
-                hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => acc.add_module(ctx, m, name),
+                hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
+                    acc.add_module(ctx, path_ctx, m, name)
+                }
                 _ => (),
             });
             acc.add_nameref_keywords_with_colon(ctx);
diff --git a/crates/ide-completion/src/completions/attribute/derive.rs b/crates/ide-completion/src/completions/attribute/derive.rs
index 21298b6ca5..48eb76029f 100644
--- a/crates/ide-completion/src/completions/attribute/derive.rs
+++ b/crates/ide-completion/src/completions/attribute/derive.rs
@@ -35,12 +35,14 @@ pub(crate) fn complete_derive_path(
                     {
                         acc.add_macro(ctx, path_ctx, mac, name)
                     }
-                    ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => acc.add_module(ctx, m, name),
+                    ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
+                        acc.add_module(ctx, path_ctx, m, name)
+                    }
                     _ => (),
                 }
             }
         }
-        Qualified::Absolute => acc.add_crate_roots(ctx),
+        Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
         // only show modules in a fresh UseTree
         Qualified::No => {
             ctx.process_all_names(&mut |name, def| {
@@ -51,7 +53,7 @@ pub(crate) fn complete_derive_path(
                         mac
                     }
                     ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
-                        return acc.add_module(ctx, m, name);
+                        return acc.add_module(ctx, path_ctx, m, name);
                     }
                     _ => return,
                 };
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index b58a9f39f2..bf0bce2198 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -29,7 +29,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext, dot_a
             acc,
             ctx,
             &receiver_ty,
-            |acc, field, ty| acc.add_field(ctx, None, field, &ty),
+            |acc, field, ty| acc.add_field(ctx, dot_access, None, field, &ty),
             |acc, field, ty| acc.add_tuple_field(ctx, None, field, &ty),
         );
     }
@@ -64,7 +64,19 @@ pub(crate) fn complete_undotted_self(
         acc,
         ctx,
         &ty,
-        |acc, field, ty| acc.add_field(ctx, Some(hir::known::SELF_PARAM), field, &ty),
+        |acc, field, ty| {
+            acc.add_field(
+                ctx,
+                &DotAccess {
+                    receiver: None,
+                    receiver_ty: None,
+                    kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal: false },
+                },
+                Some(hir::known::SELF_PARAM),
+                field,
+                &ty,
+            )
+        },
         |acc, field, ty| acc.add_tuple_field(ctx, Some(hir::known::SELF_PARAM), field, &ty),
     );
     complete_methods(ctx, &ty, |func| {
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index 9c003be6af..6b36801205 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -152,7 +152,7 @@ pub(crate) fn complete_expr_path(
                 _ => (),
             }
         }
-        Qualified::Absolute => acc.add_crate_roots(ctx),
+        Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
         Qualified::No => {
             acc.add_nameref_keywords_with_colon(ctx);
             if let Some(adt) =
diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs
index 129910465c..fa8c0eb77a 100644
--- a/crates/ide-completion/src/completions/flyimport.rs
+++ b/crates/ide-completion/src/completions/flyimport.rs
@@ -12,7 +12,7 @@ use crate::{
         CompletionContext, DotAccess, PathCompletionCtx, PathKind, PatternContext, Qualified,
         TypeLocation,
     },
-    render::{render_resolution_with_import, RenderContext},
+    render::{render_resolution_with_import, render_resolution_with_import_pat, RenderContext},
 };
 
 use super::Completions;
@@ -149,30 +149,22 @@ pub(crate) fn import_on_the_fly_path(
 pub(crate) fn import_on_the_fly_pat(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    pat_ctx: &PatternContext,
+    pattern_ctx: &PatternContext,
 ) -> Option<()> {
     if !ctx.config.enable_imports_on_the_fly {
         return None;
     }
-    if let PatternContext { record_pat: Some(_), .. } = pat_ctx {
+    if let PatternContext { record_pat: Some(_), .. } = pattern_ctx {
         return None;
     }
 
     let potential_import_name = import_name(ctx);
     let import_assets = import_assets_for_path(ctx, &potential_import_name, None)?;
 
-    import_on_the_fly(
+    import_on_the_fly_pat2(
         acc,
         ctx,
-        &PathCompletionCtx {
-            has_call_parens: false,
-            has_macro_bang: false,
-            qualified: Qualified::No,
-            parent: None,
-            kind: crate::context::PathKind::Pat { pat_ctx: pat_ctx.clone() },
-            has_type_args: false,
-            use_tree_parent: false,
-        },
+        pattern_ctx,
         import_assets,
         ctx.original_token.parent()?,
         potential_import_name,
@@ -287,6 +279,50 @@ fn import_on_the_fly(
     Some(())
 }
 
+fn import_on_the_fly_pat2(
+    acc: &mut Completions,
+    ctx: &CompletionContext,
+    pattern_ctx: &PatternContext,
+    import_assets: ImportAssets,
+    position: SyntaxNode,
+    potential_import_name: String,
+) -> Option<()> {
+    let _p = profile::span("import_on_the_fly_pat").detail(|| potential_import_name.clone());
+
+    if ImportScope::find_insert_use_container(&position, &ctx.sema).is_none() {
+        return None;
+    }
+
+    let ns_filter = |import: &LocatedImport| match import.original_item {
+        ItemInNs::Macros(mac) => mac.is_fn_like(ctx.db),
+        ItemInNs::Types(_) => true,
+        ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)),
+    };
+    let user_input_lowercased = potential_import_name.to_lowercase();
+
+    acc.add_all(
+        import_assets
+            .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
+            .into_iter()
+            .filter(ns_filter)
+            .filter(|import| {
+                !ctx.is_item_hidden(&import.item_to_import)
+                    && !ctx.is_item_hidden(&import.original_item)
+            })
+            .sorted_by_key(|located_import| {
+                compute_fuzzy_completion_order_key(
+                    &located_import.import_path,
+                    &user_input_lowercased,
+                )
+            })
+            .filter_map(|import| {
+                render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import)
+            })
+            .map(|builder| builder.build()),
+    );
+    Some(())
+}
+
 fn import_on_the_fly_method(
     acc: &mut Completions,
     ctx: &CompletionContext,
@@ -295,7 +331,7 @@ fn import_on_the_fly_method(
     position: SyntaxNode,
     potential_import_name: String,
 ) -> Option<()> {
-    let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.clone());
+    let _p = profile::span("import_on_the_fly_method").detail(|| potential_import_name.clone());
 
     if ImportScope::find_insert_use_container(&position, &ctx.sema).is_none() {
         return None;
diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs
index 329d08a9e7..4a32e0ebf5 100644
--- a/crates/ide-completion/src/completions/item_list.rs
+++ b/crates/ide-completion/src/completions/item_list.rs
@@ -45,7 +45,7 @@ pub(crate) fn complete_item_list(
                         acc.add_macro(ctx, path_ctx, m, name)
                     }
                     hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
-                        acc.add_module(ctx, m, name)
+                        acc.add_module(ctx, path_ctx, m, name)
                     }
                     _ => (),
                 }
@@ -55,13 +55,15 @@ pub(crate) fn complete_item_list(
                 acc.add_keyword(ctx, "super::");
             }
         }
-        Qualified::Absolute => acc.add_crate_roots(ctx),
+        Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
         Qualified::No if ctx.qualifier_ctx.none() => {
             ctx.process_all_names(&mut |name, def| match def {
                 hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => {
                     acc.add_macro(ctx, path_ctx, m, name)
                 }
-                hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => acc.add_module(ctx, m, name),
+                hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
+                    acc.add_module(ctx, path_ctx, m, name)
+                }
                 _ => (),
             });
             acc.add_nameref_keywords_with_colon(ctx);
diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs
index 91d5356541..4ea80a5077 100644
--- a/crates/ide-completion/src/completions/pattern.rs
+++ b/crates/ide-completion/src/completions/pattern.rs
@@ -82,20 +82,7 @@ pub(crate) fn complete_pattern(
                 hir::ModuleDef::Const(..) => refutable,
                 hir::ModuleDef::Module(..) => true,
                 hir::ModuleDef::Macro(mac) if mac.is_fn_like(ctx.db) => {
-                    return acc.add_macro(
-                        ctx,
-                        &PathCompletionCtx {
-                            has_call_parens: false,
-                            has_macro_bang: false,
-                            qualified: Qualified::No,
-                            parent: None,
-                            kind: crate::context::PathKind::Pat { pat_ctx: pattern_ctx.clone() },
-                            has_type_args: false,
-                            use_tree_parent: false,
-                        },
-                        mac,
-                        name,
-                    )
+                    return acc.add_macro_pat(ctx, pattern_ctx, mac, name);
                 }
                 _ => false,
             },
@@ -116,7 +103,7 @@ pub(crate) fn complete_pattern(
             | ScopeDef::Unknown => false,
         };
         if add_simple_path {
-            acc.add_resolution_simple(ctx, name, res);
+            acc.add_pattern_resolution(ctx, pattern_ctx, name, res);
         }
     });
 }
@@ -205,7 +192,7 @@ pub(crate) fn complete_pattern_path(
             }
         }
         // qualifier can only be none here if we are in a TuplePat or RecordPat in which case special characters have to follow the path
-        Qualified::Absolute => acc.add_crate_roots(ctx),
+        Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
         Qualified::No => {
             ctx.process_all_names(&mut |name, res| {
                 // FIXME: properly filter here
diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs
index 9f834e1ca1..8cef3a7018 100644
--- a/crates/ide-completion/src/completions/record.rs
+++ b/crates/ide-completion/src/completions/record.rs
@@ -3,7 +3,7 @@ use ide_db::SymbolKind;
 use syntax::ast::{self, Expr};
 
 use crate::{
-    context::{ExprCtx, PathCompletionCtx, PatternContext, Qualified},
+    context::{DotAccess, DotAccessKind, ExprCtx, PathCompletionCtx, PatternContext, Qualified},
     CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
     CompletionRelevancePostfixMatch, Completions,
 };
@@ -107,7 +107,17 @@ fn complete_fields(
     missing_fields: Vec<(hir::Field, hir::Type)>,
 ) {
     for (field, ty) in missing_fields {
-        acc.add_field(ctx, None, field, &ty);
+        acc.add_field(
+            ctx,
+            &DotAccess {
+                receiver: None,
+                receiver_ty: None,
+                kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal: false },
+            },
+            None,
+            field,
+            &ty,
+        );
     }
 }
 
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index dea0c701b8..c5b65d36ae 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -141,7 +141,7 @@ pub(crate) fn complete_type_path(
                 _ => (),
             }
         }
-        Qualified::Absolute => acc.add_crate_roots(ctx),
+        Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
         Qualified::No => {
             acc.add_nameref_keywords_with_colon(ctx);
             if let TypeLocation::TypeBound = location {
diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs
index 2c039d5018..c98590f136 100644
--- a/crates/ide-completion/src/completions/use_.rs
+++ b/crates/ide-completion/src/completions/use_.rs
@@ -89,7 +89,7 @@ pub(crate) fn complete_use_path(
         // fresh use tree with leading colon2, only show crate roots
         Qualified::Absolute => {
             cov_mark::hit!(use_tree_crate_roots_only);
-            acc.add_crate_roots(ctx);
+            acc.add_crate_roots(ctx, path_ctx);
         }
         // only show modules and non-std enum in a fresh UseTree
         Qualified::No => {
@@ -97,7 +97,7 @@ pub(crate) fn complete_use_path(
             ctx.process_all_names(&mut |name, res| {
                 match res {
                     ScopeDef::ModuleDef(hir::ModuleDef::Module(module)) => {
-                        acc.add_module(ctx, module, name);
+                        acc.add_module(ctx, path_ctx, module, name);
                     }
                     ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
                         // exclude prelude enum
diff --git a/crates/ide-completion/src/completions/vis.rs b/crates/ide-completion/src/completions/vis.rs
index 30de0e94f7..6621aafe4b 100644
--- a/crates/ide-completion/src/completions/vis.rs
+++ b/crates/ide-completion/src/completions/vis.rs
@@ -8,7 +8,7 @@ use crate::{
 pub(crate) fn complete_vis_path(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
+    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
     &has_in_token: &bool,
 ) {
     match qualified {
@@ -23,7 +23,7 @@ pub(crate) fn complete_vis_path(
             if let Some(next) = next_towards_current {
                 if let Some(name) = next.name(ctx.db) {
                     cov_mark::hit!(visibility_qualified);
-                    acc.add_module(ctx, next, name);
+                    acc.add_module(ctx, path_ctx, next, name);
                 }
             }
 
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 441f7ad70b..8ea03358ae 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -61,6 +61,8 @@ pub(crate) struct PathCompletionCtx {
     pub(super) qualified: Qualified,
     /// The parent of the path we are completing.
     pub(super) parent: Option<ast::Path>,
+    /// The path of which we are completing the segment
+    pub(super) path: ast::Path,
     pub(super) kind: PathKind,
     /// Whether the path segment has type args or not.
     pub(super) has_type_args: bool,
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 7e6c842b1e..e13950d56a 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -556,6 +556,7 @@ impl<'a> CompletionContext<'a> {
             has_macro_bang: false,
             qualified: Qualified::No,
             parent: path.parent_path(),
+            path: path.clone(),
             kind: PathKind::Item { kind: ItemListKind::SourceFile },
             has_type_args: false,
             use_tree_parent: false,
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 4774fe9db7..2b10dccb80 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -6,7 +6,7 @@ use hir::{Documentation, Mutability};
 use ide_db::{imports::import_assets::LocatedImport, SnippetCap, SymbolKind};
 use smallvec::SmallVec;
 use stdx::{impl_from, never};
-use syntax::{SmolStr, TextRange};
+use syntax::{SmolStr, TextRange, TextSize};
 use text_edit::TextEdit;
 
 use crate::{
@@ -68,7 +68,7 @@ pub struct CompletionItem {
 
     /// Indicates that a reference or mutable reference to this variable is a
     /// possible match.
-    ref_match: Option<Mutability>,
+    ref_match: Option<(Mutability, TextSize)>,
 
     /// The import data to add to completion's edits.
     import_to_add: SmallVec<[LocatedImport; 1]>,
@@ -104,8 +104,8 @@ impl fmt::Debug for CompletionItem {
             s.field("relevance", &self.relevance);
         }
 
-        if let Some(mutability) = &self.ref_match {
-            s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref()));
+        if let Some((mutability, offset)) = &self.ref_match {
+            s.field("ref_match", &format!("&{}@{offset:?}", mutability.as_keyword_for_ref()));
         }
         if self.trigger_call_info {
             s.field("trigger_call_info", &true);
@@ -395,14 +395,14 @@ impl CompletionItem {
         self.trigger_call_info
     }
 
-    pub fn ref_match(&self) -> Option<(Mutability, CompletionRelevance)> {
+    pub fn ref_match(&self) -> Option<(Mutability, TextSize, CompletionRelevance)> {
         // Relevance of the ref match should be the same as the original
         // match, but with exact type match set because self.ref_match
         // is only set if there is an exact type match.
         let mut relevance = self.relevance;
         relevance.type_match = Some(CompletionRelevanceTypeMatch::Exact);
 
-        self.ref_match.map(|mutability| (mutability, relevance))
+        self.ref_match.map(|(mutability, offset)| (mutability, offset, relevance))
     }
 
     pub fn imports_to_add(&self) -> &[LocatedImport] {
@@ -428,7 +428,7 @@ pub(crate) struct Builder {
     deprecated: bool,
     trigger_call_info: bool,
     relevance: CompletionRelevance,
-    ref_match: Option<Mutability>,
+    ref_match: Option<(Mutability, TextSize)>,
 }
 
 impl Builder {
@@ -548,8 +548,8 @@ impl Builder {
         self.imports_to_add.push(import_to_add);
         self
     }
-    pub(crate) fn ref_match(&mut self, mutability: Mutability) -> &mut Builder {
-        self.ref_match = Some(mutability);
+    pub(crate) fn ref_match(&mut self, mutability: Mutability, offset: TextSize) -> &mut Builder {
+        self.ref_match = Some((mutability, offset));
         self
     }
 }
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 9c339a13e7..6571e67352 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -14,12 +14,16 @@ use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef};
 use ide_db::{
     helpers::item_name, imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind,
 };
-use syntax::{SmolStr, SyntaxKind, TextRange};
+use syntax::{AstNode, SmolStr, SyntaxKind, TextRange};
 
 use crate::{
-    context::{PathCompletionCtx, PathKind},
+    context::{DotAccess, PathCompletionCtx, PathKind, PatternContext},
     item::{Builder, CompletionRelevanceTypeMatch},
-    render::{function::render_fn, literal::render_variant_lit, macro_::render_macro},
+    render::{
+        function::render_fn,
+        literal::render_variant_lit,
+        macro_::{render_macro, render_macro_pat},
+    },
     CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
 };
 /// Interface for data and methods required for items rendering.
@@ -106,6 +110,7 @@ impl<'a> RenderContext<'a> {
 
 pub(crate) fn render_field(
     ctx: RenderContext<'_>,
+    dot_access: &DotAccess,
     receiver: Option<hir::Name>,
     field: hir::Field,
     ty: &hir::Type,
@@ -130,10 +135,10 @@ pub(crate) fn render_field(
     if is_keyword && !matches!(name.as_str(), "self" | "crate" | "super" | "Self") {
         item.insert_text(format!("r#{}", name));
     }
-    if let Some(_ref_match) = compute_ref_match(ctx.completion, ty) {
-        // FIXME
-        // For now we don't properly calculate the edits for ref match
-        // completions on struct fields, so we've disabled them. See #8058.
+    if let Some(receiver) = &dot_access.receiver {
+        if let Some(ref_match) = compute_ref_match(ctx.completion, ty) {
+            item.ref_match(ref_match, receiver.syntax().text_range().start());
+        }
     }
     item.build()
 }
@@ -153,21 +158,29 @@ pub(crate) fn render_tuple_field(
     item.build()
 }
 
+pub(crate) fn render_type_inference(ty_string: String, ctx: &CompletionContext) -> CompletionItem {
+    let mut builder =
+        CompletionItem::new(CompletionItemKind::InferredType, ctx.source_range(), ty_string);
+    builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() });
+    builder.build()
+}
+
 pub(crate) fn render_path_resolution(
     ctx: RenderContext<'_>,
     path_ctx: &PathCompletionCtx,
     local_name: hir::Name,
     resolution: ScopeDef,
 ) -> Builder {
-    render_resolution_(ctx, path_ctx, local_name, None, resolution)
+    render_resolution_path(ctx, path_ctx, local_name, None, resolution)
 }
 
-pub(crate) fn render_resolution_simple(
+pub(crate) fn render_pattern_resolution(
     ctx: RenderContext<'_>,
+    pattern_ctx: &PatternContext,
     local_name: hir::Name,
     resolution: ScopeDef,
 ) -> Builder {
-    render_resolution_simple_(ctx, local_name, None, resolution)
+    render_resolution_pat(ctx, pattern_ctx, local_name, None, resolution)
 }
 
 pub(crate) fn render_resolution_with_import(
@@ -176,23 +189,56 @@ pub(crate) fn render_resolution_with_import(
     import_edit: LocatedImport,
 ) -> Option<Builder> {
     let resolution = ScopeDef::from(import_edit.original_item);
-    let local_name = match resolution {
+    let local_name = scope_def_to_name(resolution, &ctx, &import_edit)?;
+
+    Some(render_resolution_path(ctx, path_ctx, local_name, Some(import_edit), resolution))
+}
+
+pub(crate) fn render_resolution_with_import_pat(
+    ctx: RenderContext<'_>,
+    pattern_ctx: &PatternContext,
+    import_edit: LocatedImport,
+) -> Option<Builder> {
+    let resolution = ScopeDef::from(import_edit.original_item);
+    let local_name = scope_def_to_name(resolution, &ctx, &import_edit)?;
+    Some(render_resolution_pat(ctx, pattern_ctx, local_name, Some(import_edit), resolution))
+}
+
+fn scope_def_to_name(
+    resolution: ScopeDef,
+    ctx: &RenderContext,
+    import_edit: &LocatedImport,
+) -> Option<hir::Name> {
+    Some(match resolution {
         ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db),
         ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?,
         ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db),
         _ => item_name(ctx.db(), import_edit.original_item)?,
-    };
-    Some(render_resolution_(ctx, path_ctx, local_name, Some(import_edit), resolution))
+    })
 }
 
-pub(crate) fn render_type_inference(ty_string: String, ctx: &CompletionContext) -> CompletionItem {
-    let mut builder =
-        CompletionItem::new(CompletionItemKind::InferredType, ctx.source_range(), ty_string);
-    builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() });
-    builder.build()
+fn render_resolution_pat(
+    ctx: RenderContext<'_>,
+    pattern_ctx: &PatternContext,
+    local_name: hir::Name,
+    import_to_add: Option<LocatedImport>,
+    resolution: ScopeDef,
+) -> Builder {
+    let _p = profile::span("render_resolution");
+    use hir::ModuleDef::*;
+
+    match resolution {
+        ScopeDef::ModuleDef(Macro(mac)) => {
+            let ctx = ctx.import_to_add(import_to_add);
+            return render_macro_pat(ctx, pattern_ctx, local_name, mac);
+        }
+        _ => (),
+    }
+
+    render_resolution_simple_(ctx, local_name, import_to_add, resolution)
 }
 
-fn render_resolution_(
+fn render_resolution_path(
     ctx: RenderContext<'_>,
     path_ctx: &PathCompletionCtx,
     local_name: hir::Name,
@@ -221,19 +267,12 @@ fn render_resolution_(
         }
         _ => (),
     }
-    render_resolution_simple_type(ctx, path_ctx, local_name, import_to_add, resolution)
-}
 
-fn render_resolution_simple_type(
-    ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
-    local_name: hir::Name,
-    import_to_add: Option<LocatedImport>,
-    resolution: ScopeDef,
-) -> Builder {
+    let completion = ctx.completion;
     let cap = ctx.snippet_cap();
-    let db = ctx.completion.db;
-    let config = ctx.completion.config;
+    let db = completion.db;
+    let config = completion.config;
+
     let name = local_name.to_smol_str();
     let mut item = render_resolution_simple_(ctx, local_name, import_to_add, resolution);
     // Add `<>` for generic types
@@ -250,6 +289,7 @@ fn render_resolution_simple_type(
                 }
                 _ => false,
             };
+
             if has_non_default_type_params {
                 cov_mark::hit!(inserts_angle_brackets_for_generics);
                 item.lookup_by(name.clone())
@@ -259,6 +299,23 @@ fn render_resolution_simple_type(
             }
         }
     }
+    if let ScopeDef::Local(local) = resolution {
+        let ty = local.ty(db);
+        if !ty.is_unknown() {
+            item.detail(ty.display(db).to_string());
+        }
+
+        item.set_relevance(CompletionRelevance {
+            type_match: compute_type_match(completion, &ty),
+            exact_name_match: compute_exact_name_match(completion, &name),
+            is_local: true,
+            ..CompletionRelevance::default()
+        });
+
+        if let Some(ref_match) = compute_ref_match(completion, &ty) {
+            item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
+        }
+    };
     item
 }
 
@@ -269,11 +326,25 @@ fn render_resolution_simple_(
     resolution: ScopeDef,
 ) -> Builder {
     let _p = profile::span("render_resolution");
-    use hir::ModuleDef::*;
 
     let db = ctx.db();
     let ctx = ctx.import_to_add(import_to_add);
-    let kind = match resolution {
+    let kind = res_to_kind(resolution);
+
+    let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.to_smol_str());
+    item.set_relevance(ctx.completion_relevance())
+        .set_documentation(scope_def_docs(db, resolution))
+        .set_deprecated(scope_def_is_deprecated(&ctx, resolution));
+
+    if let Some(import_to_add) = ctx.import_to_add {
+        item.add_import(import_to_add);
+    }
+    item
+}
+
+fn res_to_kind(resolution: ScopeDef) -> CompletionItemKind {
+    use hir::ModuleDef::*;
+    match resolution {
         ScopeDef::Unknown => CompletionItemKind::UnresolvedReference,
         ScopeDef::ModuleDef(Function(_)) => CompletionItemKind::SymbolKind(SymbolKind::Function),
         ScopeDef::ModuleDef(Variant(_)) => CompletionItemKind::SymbolKind(SymbolKind::Variant),
@@ -299,36 +370,7 @@ fn render_resolution_simple_(
         ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => {
             CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
         }
-    };
-
-    let local_name = local_name.to_smol_str();
-    let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.clone());
-    item.set_relevance(ctx.completion_relevance());
-    if let ScopeDef::Local(local) = resolution {
-        let ty = local.ty(db);
-        if !ty.is_unknown() {
-            item.detail(ty.display(db).to_string());
-        }
-
-        item.set_relevance(CompletionRelevance {
-            type_match: compute_type_match(ctx.completion, &ty),
-            exact_name_match: compute_exact_name_match(ctx.completion, &local_name),
-            is_local: true,
-            ..CompletionRelevance::default()
-        });
-
-        if let Some(ref_match) = compute_ref_match(ctx.completion, &ty) {
-            item.ref_match(ref_match);
-        }
-    };
-
-    item.set_documentation(scope_def_docs(db, resolution))
-        .set_deprecated(scope_def_is_deprecated(&ctx, resolution));
-
-    if let Some(import_to_add) = ctx.import_to_add {
-        item.add_import(import_to_add);
     }
-    item
 }
 
 fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<hir::Documentation> {
@@ -455,7 +497,7 @@ mod tests {
                 let relevance = display_relevance(it.relevance());
                 items.push(format!("{} {} {}\n", tag, it.label(), relevance));
 
-                if let Some((mutability, relevance)) = it.ref_match() {
+                if let Some((mutability, _offset, relevance)) = it.ref_match() {
                     let label = format!("&{}{}", mutability.as_keyword_for_ref(), it.label());
                     let relevance = display_relevance(relevance);
 
@@ -1494,9 +1536,6 @@ impl Foo { fn baz(&self) -> u32 { 0 } }
 fn foo(f: Foo) { let _: &u32 = f.b$0 }
 "#,
             &[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)],
-            // FIXME
-            // Ideally we'd also suggest &f.bar and &f.baz() as exact
-            // type matches. See #8058.
             expect![[r#"
                 [
                     CompletionItem {
@@ -1507,6 +1546,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
                         kind: Method,
                         lookup: "baz",
                         detail: "fn(&self) -> u32",
+                        ref_match: "&@96",
                     },
                     CompletionItem {
                         label: "bar",
@@ -1517,6 +1557,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
                             Field,
                         ),
                         detail: "u32",
+                        ref_match: "&@96",
                     },
                 ]
             "#]],
@@ -1525,7 +1566,6 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
 
     #[test]
     fn qualified_path_ref() {
-        // disabled right now because it doesn't render correctly, #8058
         check_kinds(
             r#"
 struct S;
@@ -1554,6 +1594,7 @@ fn main() {
                         ),
                         lookup: "foo",
                         detail: "fn() -> S",
+                        ref_match: "&@92",
                     },
                 ]
             "#]],
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index 3666ee40e2..37486e4d93 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -4,20 +4,19 @@ use hir::{db::HirDatabase, AsAssocItem, HirDisplay};
 use ide_db::{SnippetCap, SymbolKind};
 use itertools::Itertools;
 use stdx::{format_to, to_lower_snake_case};
-use syntax::SmolStr;
+use syntax::{AstNode, SmolStr};
 
 use crate::{
-    context::{
-        CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind, Qualified,
-    },
+    context::{CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind},
     item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
     render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext},
     CallableSnippets,
 };
 
-enum FuncKind {
-    Function,
-    Method(Option<hir::Name>),
+#[derive(Debug)]
+enum FuncKind<'ctx> {
+    Function(&'ctx PathCompletionCtx),
+    Method(&'ctx DotAccess, Option<hir::Name>),
 }
 
 pub(crate) fn render_fn(
@@ -27,29 +26,7 @@ pub(crate) fn render_fn(
     func: hir::Function,
 ) -> Builder {
     let _p = profile::span("render_fn");
-    let func_kind = FuncKind::Function;
-    let params = match ctx.completion.config.snippet_cap {
-        Some(_) => {
-            if !matches!(
-                path_ctx,
-                PathCompletionCtx { kind: PathKind::Expr { .. }, has_call_parens: true, .. }
-                    | PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. }
-            ) {
-                params(ctx.completion, func, &func_kind, false)
-            } else {
-                None
-            }
-        }
-        _ => None,
-    };
-    render(
-        ctx,
-        local_name,
-        func,
-        func_kind,
-        params,
-        matches!(path_ctx.qualified, Qualified::With { .. }),
-    )
+    render(ctx, local_name, func, FuncKind::Function(path_ctx))
 }
 
 pub(crate) fn render_method(
@@ -60,16 +37,7 @@ pub(crate) fn render_method(
     func: hir::Function,
 ) -> Builder {
     let _p = profile::span("render_method");
-    let func_kind = FuncKind::Method(receiver);
-    let params = match ctx.completion.config.snippet_cap {
-        Some(_) => match dot_access {
-            DotAccess { kind: DotAccessKind::Method { has_parens: true }, .. } => None,
-            _ => params(ctx.completion, func, &func_kind, true),
-        },
-        _ => None,
-    };
-
-    render(ctx, local_name, func, func_kind, params, false)
+    render(ctx, local_name, func, FuncKind::Method(dot_access, receiver))
 }
 
 fn render(
@@ -77,15 +45,13 @@ fn render(
     local_name: Option<hir::Name>,
     func: hir::Function,
     func_kind: FuncKind,
-    params: Option<(Option<hir::SelfParam>, Vec<hir::Param>)>,
-    qualified_path: bool,
 ) -> Builder {
     let db = completion.db;
 
     let name = local_name.unwrap_or_else(|| func.name(db));
 
     let call = match &func_kind {
-        FuncKind::Method(Some(receiver)) => format!("{}.{}", receiver, &name).into(),
+        FuncKind::Method(_, Some(receiver)) => format!("{}.{}", receiver, &name).into(),
         _ => name.to_smol_str(),
     };
     let mut item = CompletionItem::new(
@@ -111,11 +77,14 @@ fn render(
     });
 
     if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
-        // FIXME For now we don't properly calculate the edits for ref match
-        // completions on methods or qualified paths, so we've disabled them.
-        // See #8058.
-        if matches!(func_kind, FuncKind::Function) && !qualified_path {
-            item.ref_match(ref_match);
+        match func_kind {
+            FuncKind::Function(path_ctx) => {
+                item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
+            }
+            FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => {
+                item.ref_match(ref_match, receiver.syntax().text_range().start());
+            }
+            _ => (),
         }
     }
 
@@ -124,12 +93,34 @@ fn render(
         .detail(detail(db, func))
         .lookup_by(name.to_smol_str());
 
-    match completion.config.snippet_cap.zip(params) {
-        Some((cap, (self_param, params))) => {
-            add_call_parens(&mut item, completion, cap, call, self_param, params);
+    match ctx.completion.config.snippet_cap {
+        Some(cap) => {
+            let complete_params = match func_kind {
+                FuncKind::Function(PathCompletionCtx {
+                    kind: PathKind::Expr { .. },
+                    has_call_parens: false,
+                    ..
+                }) => Some(false),
+                FuncKind::Method(
+                    DotAccess {
+                        kind:
+                            DotAccessKind::Method { has_parens: false } | DotAccessKind::Field { .. },
+                        ..
+                    },
+                    _,
+                ) => Some(true),
+                _ => None,
+            };
+            if let Some(has_dot_receiver) = complete_params {
+                if let Some((self_param, params)) =
+                    params(ctx.completion, func, &func_kind, has_dot_receiver)
+                {
+                    add_call_parens(&mut item, completion, cap, call, self_param, params);
+                }
+            }
         }
         _ => (),
-    }
+    };
 
     match ctx.import_to_add {
         Some(import_to_add) => {
@@ -291,7 +282,7 @@ fn params(
         }
     }
 
-    let self_param = if has_dot_receiver || matches!(func_kind, FuncKind::Method(Some(_))) {
+    let self_param = if has_dot_receiver || matches!(func_kind, FuncKind::Method(_, Some(_))) {
         None
     } else {
         func.self_param(ctx.db)
diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs
index b89030990e..7b0555d5a4 100644
--- a/crates/ide-completion/src/render/literal.rs
+++ b/crates/ide-completion/src/render/literal.rs
@@ -2,6 +2,7 @@
 
 use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
 use ide_db::SymbolKind;
+use syntax::AstNode;
 
 use crate::{
     context::{CompletionContext, PathCompletionCtx, PathKind},
@@ -117,7 +118,7 @@ fn render(
         ..ctx.completion_relevance()
     });
     if let Some(ref_match) = compute_ref_match(completion, &ty) {
-        item.ref_match(ref_match);
+        item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
     }
 
     if let Some(import_to_add) = ctx.import_to_add {
diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs
index 6da7bb3193..ac2091eca9 100644
--- a/crates/ide-completion/src/render/macro_.rs
+++ b/crates/ide-completion/src/render/macro_.rs
@@ -5,24 +5,37 @@ use ide_db::SymbolKind;
 use syntax::SmolStr;
 
 use crate::{
-    context::{PathCompletionCtx, PathKind},
+    context::{PathCompletionCtx, PathKind, PatternContext},
     item::{Builder, CompletionItem},
     render::RenderContext,
 };
 
 pub(crate) fn render_macro(
     ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
+    PathCompletionCtx { kind, has_macro_bang, has_call_parens, .. }: &PathCompletionCtx,
+
+    name: hir::Name,
+    macro_: hir::Macro,
+) -> Builder {
+    let _p = profile::span("render_macro");
+    render(ctx, *kind == PathKind::Use, *has_macro_bang, *has_call_parens, name, macro_)
+}
+
+pub(crate) fn render_macro_pat(
+    ctx: RenderContext<'_>,
+    _pattern_ctx: &PatternContext,
     name: hir::Name,
     macro_: hir::Macro,
 ) -> Builder {
     let _p = profile::span("render_macro");
-    render(ctx, path_ctx, name, macro_)
+    render(ctx, false, false, false, name, macro_)
 }
 
 fn render(
     ctx @ RenderContext { completion, .. }: RenderContext<'_>,
-    PathCompletionCtx { kind, has_macro_bang, has_call_parens, .. }: &PathCompletionCtx,
+    is_use_path: bool,
+    has_macro_bang: bool,
+    has_call_parens: bool,
     name: hir::Name,
     macro_: hir::Macro,
 ) -> Builder {
@@ -39,7 +52,7 @@ fn render(
     let is_fn_like = macro_.is_fn_like(completion.db);
     let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") };
 
-    let needs_bang = is_fn_like && *kind != PathKind::Use && !has_macro_bang;
+    let needs_bang = is_fn_like && !is_use_path && !has_macro_bang;
 
     let mut item = CompletionItem::new(
         SymbolKind::from(macro_.kind(completion.db)),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 3d702fe8dc..cdd152ccf5 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -224,6 +224,7 @@ fn completion_item(
     max_relevance: u32,
     item: CompletionItem,
 ) {
+    let insert_replace_support = config.insert_replace_support().then(|| tdpp.position);
     let mut additional_text_edits = Vec::new();
 
     // LSP does not allow arbitrary edits in completion, so we have to do a
@@ -233,7 +234,6 @@ fn completion_item(
         let source_range = item.source_range();
         for indel in item.text_edit().iter() {
             if indel.delete.contains_range(source_range) {
-                let insert_replace_support = config.insert_replace_support().then(|| tdpp.position);
                 text_edit = Some(if indel.delete == source_range {
                     self::completion_text_edit(line_index, insert_replace_support, indel.clone())
                 } else {
@@ -254,6 +254,14 @@ fn completion_item(
         text_edit.unwrap()
     };
 
+    let insert_text_format = item.is_snippet().then(|| lsp_types::InsertTextFormat::SNIPPET);
+    let tags = item.deprecated().then(|| vec![lsp_types::CompletionItemTag::DEPRECATED]);
+    let command = if item.trigger_call_info() && config.client_commands().trigger_parameter_hints {
+        Some(command::trigger_parameter_hints())
+    } else {
+        None
+    };
+
     let mut lsp_item = lsp_types::CompletionItem {
         label: item.label().to_string(),
         detail: item.detail().map(|it| it.to_string()),
@@ -263,22 +271,14 @@ fn completion_item(
         additional_text_edits: Some(additional_text_edits),
         documentation: item.documentation().map(documentation),
         deprecated: Some(item.deprecated()),
+        tags,
+        command,
+        insert_text_format,
         ..Default::default()
     };
 
     set_score(&mut lsp_item, max_relevance, item.relevance());
 
-    if item.deprecated() {
-        lsp_item.tags = Some(vec![lsp_types::CompletionItemTag::DEPRECATED])
-    }
-
-    if item.trigger_call_info() && config.client_commands().trigger_parameter_hints {
-        lsp_item.command = Some(command::trigger_parameter_hints());
-    }
-
-    if item.is_snippet() {
-        lsp_item.insert_text_format = Some(lsp_types::InsertTextFormat::SNIPPET);
-    }
     if config.completion().enable_imports_on_the_fly {
         if let imports @ [_, ..] = item.imports_to_add() {
             let imports: Vec<_> = imports
@@ -299,18 +299,17 @@ fn completion_item(
         }
     }
 
-    if let Some((mutability, relevance)) = item.ref_match() {
+    if let Some((mutability, offset, relevance)) = item.ref_match() {
         let mut lsp_item_with_ref = lsp_item.clone();
         set_score(&mut lsp_item_with_ref, max_relevance, relevance);
         lsp_item_with_ref.label =
             format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label);
-        if let Some(it) = &mut lsp_item_with_ref.text_edit {
-            let new_text = match it {
-                lsp_types::CompletionTextEdit::Edit(it) => &mut it.new_text,
-                lsp_types::CompletionTextEdit::InsertAndReplace(it) => &mut it.new_text,
-            };
-            *new_text = format!("&{}{}", mutability.as_keyword_for_ref(), new_text);
-        }
+        lsp_item_with_ref.additional_text_edits.get_or_insert_with(Default::default).push(
+            self::text_edit(
+                line_index,
+                Indel::insert(offset, format!("&{}", mutability.as_keyword_for_ref())),
+            ),
+        );
 
         acc.push(lsp_item_with_ref);
     };
diff --git a/xtask/src/release.rs b/xtask/src/release.rs
index 1c5fc64c24..04544d6f6b 100644
--- a/xtask/src/release.rs
+++ b/xtask/src/release.rs
@@ -36,15 +36,7 @@ impl flags::Release {
 
         let today = date_iso(sh)?;
         let commit = cmd!(sh, "git rev-parse HEAD").read()?;
-        let changelog_n = sh
-            .read_dir(changelog_dir.as_path())?
-            .into_iter()
-            .filter_map(|p| p.file_stem().map(|s| s.to_string_lossy().to_string()))
-            .filter_map(|s| s.splitn(5, '-').last().map(|n| n.replace('-', ".")))
-            .filter_map(|s| s.parse::<f32>().ok())
-            .map(|n| 1 + n.floor() as usize)
-            .max()
-            .unwrap_or_default();
+        let changelog_n = sh.read_dir(changelog_dir.as_path())?.len();
 
         for adoc in [
             "manual.adoc",


Test patch:
diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs
index da9e5e2028..8953f1b2fd 100644
--- a/crates/ide-completion/src/tests/pattern.rs
+++ b/crates/ide-completion/src/tests/pattern.rs
@@ -399,6 +399,7 @@ fn foo() {
 
 #[test]
 fn completes_no_delims_if_existing() {
+    // FIXME: We should not complete functions here
     check_empty(
         r#"
 struct Bar(u32);
@@ -409,7 +410,7 @@ fn foo() {
 }
 "#,
         expect![[r#"
-            fn foo()   fn()
+            fn foo     fn()
             st Bar
             bt u32
             kw crate::
@@ -427,7 +428,7 @@ fn foo() {
 }
 "#,
         expect![[r#"
-            fn foo()   fn()
+            fn foo     fn()
             st Foo
             bt u32
             kw crate::