Repository: rust-analyzer/rust-analyzer
Pull request #11093 (2021-12-22 01:26:21)

fix: Do not complete `Drop::drop`, complete `std::mem::drop` instead
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/5005
bors r+


Patch:
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index ad647d045c..91a6210d26 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -100,25 +100,14 @@ jobs:
       - run: npm ci
         working-directory: editors/code
 
-      - name: Package Extension (release)
-        if: github.ref == 'refs/heads/release'
-        run: npx vsce package -o "../../dist/rust-analyzer-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }}
-        working-directory: editors/code
-
-      - name: Package Extension (nightly)
-        if: github.ref != 'refs/heads/release'
-        run: npx vsce package -o "../../dist/rust-analyzer-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }} --pre-release
+      - run: npx vsce package -o "../../dist/rust-analyzer-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }}
         working-directory: editors/code
 
       - if: matrix.target == 'x86_64-unknown-linux-gnu'
         run: rm -rf editors/code/server
 
-      - if: matrix.target == 'x86_64-unknown-linux-gnu' && github.ref == 'refs/heads/release'
-        run: npx vsce package -o ../../dist/rust-analyzer-no-server.vsix
-        working-directory: editors/code
-
-      - if: matrix.target == 'x86_64-unknown-linux-gnu' && github.ref != 'refs/heads/release'
-        run: npx vsce package -o ../../dist/rust-analyzer-no-server.vsix --pre-release
+      - if: matrix.target == 'x86_64-unknown-linux-gnu'
+        run: npx vsce package -o ../../dist/rust-analyzer.vsix
         working-directory: editors/code
 
       - name: Run analysis-stats on rust-analyzer
@@ -162,14 +151,7 @@ jobs:
       - run: npm ci
         working-directory: editors/code
 
-      - name: Publish Extension (release)
-        if: github.ref == 'refs/heads/release'
-        run: npx vsce package -o "../../dist/rust-analyzer-alpine-x64.vsix" --target alpine-x64
-        working-directory: editors/code
-
-      - name: Publish Extension (nightly)
-        if: github.ref != 'refs/heads/release'
-        run: npx vsce package -o "../../dist/rust-analyzer-alpine-x64.vsix" --target alpine-x64 --pre-release
+      - run: npx vsce package -o "../../dist/rust-analyzer-alpine-x64.vsix" --target alpine-x64
         working-directory: editors/code
 
       - run: rm -rf editors/code/server
@@ -241,19 +223,11 @@ jobs:
           name: ${{ env.TAG }}
           token: ${{ secrets.GITHUB_TOKEN }}
 
-      - run: rm dist/rust-analyzer-no-server.vsix
-
       - run: npm ci
         working-directory: ./editors/code
 
-      - name: Publish Extension (release)
+      - name: Publish Extension
         if: github.ref == 'refs/heads/release'
         working-directory: ./editors/code
         # token from https://dev.azure.com/rust-analyzer/
         run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
-
-      - name: Publish Extension (nightly)
-        # check specifically for nightly in case someone triggers a release on a feature branch
-        if: github.ref == 'refs/heads/nightly'
-        working-directory: ./editors/code
-        run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix --pre-release
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index 5e91a3df0a..27a05b87c1 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -72,12 +72,8 @@ impl ImportMap {
 
         let mut import_map = collect_import_map(db, krate);
 
-        let mut importables = import_map
-            .map
-            .iter()
-            .map(|(item, info)| (item, fst_path(&info.path)))
-            .collect::<Vec<_>>();
-        importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2));
+        let mut importables = import_map.map.iter().collect::<Vec<_>>();
+        importables.sort_by_cached_key(|(_, import_info)| fst_path(&import_info.path));
 
         // Build the FST, taking care not to insert duplicate values.
 
@@ -85,20 +81,20 @@ impl ImportMap {
         let mut last_batch_start = 0;
 
         for idx in 0..importables.len() {
-            let key = &importables[last_batch_start].1;
-            if let Some((_, fst_path)) = importables.get(idx + 1) {
-                if key == fst_path {
+            let key = fst_path(&importables[last_batch_start].1.path);
+            if let Some((_, next_import_info)) = importables.get(idx + 1) {
+                if key == fst_path(&next_import_info.path) {
                     continue;
                 }
             }
 
-            let _ = builder.insert(key, last_batch_start as u64);
+            builder.insert(key, last_batch_start as u64).unwrap();
 
             last_batch_start = idx + 1;
         }
 
-        import_map.fst = builder.into_map();
-        import_map.importables = importables.iter().map(|&(&item, _)| item).collect();
+        import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
+        import_map.importables = importables.iter().map(|(item, _)| **item).collect();
 
         Arc::new(import_map)
     }
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 45b374f338..aafda49693 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -56,7 +56,6 @@ use la_arena::{Arena, Idx, IdxRange, RawIdx};
 use profile::Count;
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
-use stdx::never;
 use syntax::{ast, match_ast, SyntaxKind};
 
 use crate::{
@@ -110,17 +109,18 @@ impl ItemTree {
             Some(node) => node,
             None => return Default::default(),
         };
-        if never!(syntax.kind() == SyntaxKind::ERROR) {
+        if syntax.kind() == SyntaxKind::ERROR {
             // FIXME: not 100% sure why these crop up, but return an empty tree to avoid a panic
             return Default::default();
         }
 
-        let ctx = lower::Ctx::new(db, file_id);
+        let hygiene = Hygiene::new(db.upcast(), file_id);
+        let ctx = lower::Ctx::new(db, hygiene.clone(), file_id);
         let mut top_attrs = None;
         let mut item_tree = match_ast! {
             match syntax {
                 ast::SourceFile(file) => {
-                    top_attrs = Some(RawAttrs::new(db, &file, ctx.hygiene()));
+                    top_attrs = Some(RawAttrs::new(db, &file, &hygiene));
                     ctx.lower_module_items(&file)
                 },
                 ast::MacroItems(items) => {
@@ -147,7 +147,8 @@ impl ItemTree {
     fn block_item_tree(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
         let loc = db.lookup_intern_block(block);
         let block = loc.ast_id.to_node(db.upcast());
-        let ctx = lower::Ctx::new(db, loc.ast_id.file_id);
+        let hygiene = Hygiene::new(db.upcast(), loc.ast_id.file_id);
+        let ctx = lower::Ctx::new(db, hygiene, loc.ast_id.file_id);
         Arc::new(ctx.lower_block(&block))
     }
 
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index f31fcf0d97..acc26bcf63 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -19,26 +19,24 @@ fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
 pub(super) struct Ctx<'a> {
     db: &'a dyn DefDatabase,
     tree: ItemTree,
+    hygiene: Hygiene,
     source_ast_id_map: Arc<AstIdMap>,
     body_ctx: crate::body::LowerCtx<'a>,
     forced_visibility: Option<RawVisibilityId>,
 }
 
 impl<'a> Ctx<'a> {
-    pub(super) fn new(db: &'a dyn DefDatabase, file: HirFileId) -> Self {
+    pub(super) fn new(db: &'a dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self {
         Self {
             db,
             tree: ItemTree::default(),
+            hygiene,
             source_ast_id_map: db.ast_id_map(file),
             body_ctx: crate::body::LowerCtx::new(db, file),
             forced_visibility: None,
         }
     }
 
-    pub(super) fn hygiene(&self) -> &Hygiene {
-        self.body_ctx.hygiene()
-    }
-
     pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree {
         self.tree.top_level =
             item_owner.items().flat_map(|item| self.lower_mod_item(&item)).collect();
@@ -90,7 +88,7 @@ impl<'a> Ctx<'a> {
     }
 
     fn lower_mod_item(&mut self, item: &ast::Item) -> Option<ModItem> {
-        let attrs = RawAttrs::new(self.db, item, self.hygiene());
+        let attrs = RawAttrs::new(self.db, item, &self.hygiene);
         let item: ModItem = match item {
             ast::Item::Struct(ast) => self.lower_struct(ast)?.into(),
             ast::Item::Union(ast) => self.lower_union(ast)?.into(),
@@ -164,7 +162,7 @@ impl<'a> Ctx<'a> {
         for field in fields.fields() {
             if let Some(data) = self.lower_record_field(&field) {
                 let idx = self.data().fields.alloc(data);
-                self.add_attrs(idx.into(), RawAttrs::new(self.db, &field, self.hygiene()));
+                self.add_attrs(idx.into(), RawAttrs::new(self.db, &field, &self.hygiene));
             }
         }
         let end = self.next_field_idx();
@@ -184,7 +182,7 @@ impl<'a> Ctx<'a> {
         for (i, field) in fields.fields().enumerate() {
             let data = self.lower_tuple_field(i, &field);
             let idx = self.data().fields.alloc(data);
-            self.add_attrs(idx.into(), RawAttrs::new(self.db, &field, self.hygiene()));
+            self.add_attrs(idx.into(), RawAttrs::new(self.db, &field, &self.hygiene));
         }
         let end = self.next_field_idx();
         IdxRange::new(start..end)
@@ -229,7 +227,7 @@ impl<'a> Ctx<'a> {
         for variant in variants.variants() {
             if let Some(data) = self.lower_variant(&variant) {
                 let idx = self.data().variants.alloc(data);
-                self.add_attrs(idx.into(), RawAttrs::new(self.db, &variant, self.hygiene()));
+                self.add_attrs(idx.into(), RawAttrs::new(self.db, &variant, &self.hygiene));
             }
         }
         let end = self.next_variant_idx();
@@ -272,7 +270,7 @@ impl<'a> Ctx<'a> {
                 };
                 let ty = Interned::new(self_type);
                 let idx = self.data().params.alloc(Param::Normal(None, ty));
-                self.add_attrs(idx.into(), RawAttrs::new(self.db, &self_param, self.hygiene()));
+                self.add_attrs(idx.into(), RawAttrs::new(self.db, &self_param, &self.hygiene));
                 has_self_param = true;
             }
             for param in param_list.params() {
@@ -296,7 +294,7 @@ impl<'a> Ctx<'a> {
                         self.data().params.alloc(Param::Normal(name, ty))
                     }
                 };
-                self.add_attrs(idx.into(), RawAttrs::new(self.db, &param, self.hygiene()));
+                self.add_attrs(idx.into(), RawAttrs::new(self.db, &param, &self.hygiene));
             }
         }
         let end_param = self.next_param_idx();
@@ -429,7 +427,7 @@ impl<'a> Ctx<'a> {
             self.with_inherited_visibility(visibility, |this| {
                 list.assoc_items()
                     .filter_map(|item| {
-                        let attrs = RawAttrs::new(db, &item, this.hygiene());
+                        let attrs = RawAttrs::new(db, &item, &this.hygiene);
                         this.lower_assoc_item(&item).map(|item| {
                             this.add_attrs(ModItem::from(item).into(), attrs);
                             item
@@ -467,7 +465,7 @@ impl<'a> Ctx<'a> {
             .flat_map(|it| it.assoc_items())
             .filter_map(|item| {
                 let assoc = self.lower_assoc_item(&item)?;
-                let attrs = RawAttrs::new(self.db, &item, self.hygiene());
+                let attrs = RawAttrs::new(self.db, &item, &self.hygiene);
                 self.add_attrs(ModItem::from(assoc).into(), attrs);
                 Some(assoc)
             })
@@ -480,7 +478,7 @@ impl<'a> Ctx<'a> {
     fn lower_use(&mut self, use_item: &ast::Use) -> Option<FileItemTreeId<Import>> {
         let visibility = self.lower_visibility(use_item);
         let ast_id = self.source_ast_id_map.ast_id(use_item);
-        let (use_tree, _) = lower_use_tree(self.db, self.hygiene(), use_item.use_tree()?)?;
+        let (use_tree, _) = lower_use_tree(self.db, &self.hygiene, use_item.use_tree()?)?;
 
         let res = Import { visibility, ast_id, use_tree };
         Some(id(self.data().imports.alloc(res)))
@@ -502,7 +500,7 @@ impl<'a> Ctx<'a> {
     }
 
     fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
-        let path = Interned::new(ModPath::from_src(self.db, m.path()?, self.hygiene())?);
+        let path = Interned::new(ModPath::from_src(self.db, m.path()?, &self.hygiene)?);
         let ast_id = self.source_ast_id_map.ast_id(m);
         let expand_to = hir_expand::ExpandTo::from_call_site(m);
         let res = MacroCall { path, ast_id, expand_to };
@@ -537,7 +535,7 @@ impl<'a> Ctx<'a> {
                     // (in other words, the knowledge that they're in an extern block must not be used).
                     // This is because an extern block can contain macros whose ItemTree's top-level items
                     // should be considered to be in an extern block too.
-                    let attrs = RawAttrs::new(self.db, &item, self.hygiene());
+                    let attrs = RawAttrs::new(self.db, &item, &self.hygiene);
                     let id: ModItem = match item {
                         ast::ExternItem::Fn(ast) => {
                             let func_id = self.lower_function(&ast)?;
@@ -618,9 +616,7 @@ impl<'a> Ctx<'a> {
     fn lower_visibility(&mut self, item: &dyn ast::HasVisibility) -> RawVisibilityId {
         let vis = match self.forced_visibility {
             Some(vis) => return vis,
-            None => {
-                RawVisibility::from_ast_with_hygiene(self.db, item.visibility(), self.hygiene())
-            }
+            None => RawVisibility::from_ast_with_hygiene(self.db, item.visibility(), &self.hygiene),
         };
 
         self.data().vis.alloc(vis)
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs
index 0daf114788..3cab1918f3 100644
--- a/crates/ide_completion/src/completions/attribute/derive.rs
+++ b/crates/ide_completion/src/completions/attribute/derive.rs
@@ -1,7 +1,7 @@
 //! Completion for derives
 use hir::{HasAttrs, MacroDef, MacroKind};
 use ide_db::{
-    helpers::{import_assets::ImportAssets, insert_use::ImportScope, FamousDefs},
+    helpers::{import_assets::ImportAssets, insert_use::ImportScope},
     SymbolKind,
 };
 use itertools::Itertools;
@@ -18,7 +18,7 @@ pub(super) fn complete_derive(
     ctx: &CompletionContext,
     existing_derives: &[ast::Path],
 ) {
-    let core = FamousDefs(&ctx.sema, ctx.krate).core();
+    let core = ctx.famous_defs().core();
     let existing_derives: FxHashSet<_> = existing_derives
         .into_iter()
         .filter_map(|path| ctx.scope.speculative_resolve_as_mac(&path))
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index e08a70ac7e..539b423cb3 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -76,7 +76,14 @@ fn complete_methods(
 ) {
     if let Some(krate) = ctx.krate {
         let mut seen_methods = FxHashSet::default();
-        let traits_in_scope = ctx.scope.visible_traits();
+        let mut traits_in_scope = ctx.scope.visible_traits();
+
+        // Remove drop from the environment as calling `Drop::drop` is not allowed
+        if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() {
+            cov_mark::hit!(dot_remove_drop_trait);
+            traits_in_scope.remove(&drop_trait.into());
+        }
+
         receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
             if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) {
                 f(func);
@@ -709,4 +716,34 @@ fn main() {
             "#]],
         )
     }
+
+    #[test]
+    fn postfix_drop_completion() {
+        cov_mark::check!(dot_remove_drop_trait);
+        cov_mark::check!(postfix_drop_completion);
+        check_edit(
+            "drop",
+            r#"
+//- minicore: drop
+struct Vec<T>(T);
+impl<T> Drop for Vec<T> {
+    fn drop(&mut self) {}
+}
+fn main() {
+    let x = Vec(0u32)
+    x.$0;
+}
+"#,
+            r"
+struct Vec<T>(T);
+impl<T> Drop for Vec<T> {
+    fn drop(&mut self) {}
+}
+fn main() {
+    let x = Vec(0u32)
+    drop($0x);
+}
+",
+        )
+    }
 }
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index 8a25da7ddb..a212a98461 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -2,9 +2,9 @@
 
 mod format_like;
 
-use hir::Documentation;
+use hir::{Documentation, HasAttrs};
 use ide_db::{
-    helpers::{insert_use::ImportScope, FamousDefs, SnippetCap},
+    helpers::{insert_use::ImportScope, SnippetCap},
     ty_filter::TryEnum,
 };
 use syntax::{
@@ -59,6 +59,22 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
         None => return,
     };
 
+    if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() {
+        if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) {
+            if let &[hir::AssocItem::Function(drop_fn)] = &*drop_trait.items(ctx.db) {
+                cov_mark::hit!(postfix_drop_completion);
+                // FIXME: check that `drop` is in scope, use fully qualified path if it isn't/if shadowed
+                let mut item = postfix_snippet(
+                    "drop",
+                    "fn drop(&mut self)",
+                    &format!("drop($0{})", receiver_text),
+                );
+                item.set_documentation(drop_fn.docs(ctx.db));
+                item.add_to(acc);
+            }
+        }
+    }
+
     if !ctx.config.snippets.is_empty() {
         add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
     }
@@ -107,7 +123,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
         )
         .add_to(acc);
         postfix_snippet("not", "!expr", &format!("!{}", receiver_text)).add_to(acc);
-    } else if let Some(trait_) = FamousDefs(&ctx.sema, ctx.krate).core_iter_IntoIterator() {
+    } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() {
         if receiver_ty.impls_trait(ctx.db, trait_, &[]) {
             postfix_snippet(
                 "for",
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
index d50f889ee4..b066a46065 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -1,5 +1,5 @@
 //! Complete fields in record literals and patterns.
-use ide_db::{helpers::FamousDefs, SymbolKind};
+use ide_db::SymbolKind;
 use syntax::{ast::Expr, T};
 
 use crate::{
@@ -13,7 +13,7 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
             | ImmediateLocation::RecordExprUpdate(record_expr),
         ) => {
             let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
-            let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default();
+            let default_trait = ctx.famous_defs().core_default_Default();
             let impl_default_trait = default_trait.zip(ty).map_or(false, |(default_trait, ty)| {
                 ty.original.impls_trait(ctx.db, default_trait, &[])
             });
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 2a1891c76b..d29e181bac 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -5,6 +5,7 @@ use hir::{Local, Name, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo};
 use ide_db::{
     active_parameter::ActiveParameter,
     base_db::{FilePosition, SourceDatabase},
+    helpers::FamousDefs,
     RootDatabase,
 };
 use syntax::{
@@ -150,6 +151,10 @@ impl<'a> CompletionContext<'a> {
         self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
     }
 
+    pub(crate) fn famous_defs(&self) -> FamousDefs {
+        FamousDefs(&self.sema, self.krate)
+    }
+
     pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
         match &self.completion_location {
             Some(
@@ -302,7 +307,6 @@ impl<'a> CompletionContext<'a> {
 
     /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
     pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
-        let _p = profile::span("CompletionContext::process_all_names");
         self.scope.process_all_names(&mut |name, def| {
             if self.is_scope_def_hidden(def) {
                 return;
@@ -423,7 +427,6 @@ impl<'a> CompletionContext<'a> {
         mut offset: TextSize,
         mut fake_ident_token: SyntaxToken,
     ) {
-        let _p = profile::span("CompletionContext::expand_and_fill");
         loop {
             // Expand attributes
             if let (Some(actual_item), Some(item_with_fake_ident)) = (
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 404a2fa9d3..4fa5aa04de 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -71,6 +71,7 @@ impl<'a> RenderContext<'a> {
                 .unwrap_or(false)
     }
 
+    // FIXME: remove this
     fn docs(&self, def: impl HasAttrs) -> Option<hir::Documentation> {
         def.docs(self.db())
     }
diff --git a/crates/ide_db/src/helpers/famous_defs.rs b/crates/ide_db/src/helpers/famous_defs.rs
index fd92c64f17..ee7bf9540b 100644
--- a/crates/ide_db/src/helpers/famous_defs.rs
+++ b/crates/ide_db/src/helpers/famous_defs.rs
@@ -76,6 +76,10 @@ impl FamousDefs<'_, '_> {
         self.find_enum("core:ops:ControlFlow")
     }
 
+    pub fn core_ops_Drop(&self) -> Option<Trait> {
+        self.find_trait("core:ops:Drop")
+    }
+
     pub fn core_marker_Copy(&self) -> Option<Trait> {
         self.find_trait("core:marker:Copy")
     }
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 8728654662..038a07c75a 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -17,7 +17,7 @@
             },
             "devDependencies": {
                 "@types/node": "~14.17.5",
-                "@types/vscode": "~1.63.0",
+                "@types/vscode": "~1.62.0",
                 "@typescript-eslint/eslint-plugin": "^5.5.0",
                 "@typescript-eslint/parser": "^5.5.0",
                 "@vscode/test-electron": "^1.6.2",
@@ -29,7 +29,7 @@
                 "vsce": "^2.5.1"
             },
             "engines": {
-                "vscode": "^1.63.0"
+                "vscode": "^1.62.0"
             }
         },
         "node_modules/@eslint/eslintrc": {
@@ -143,9 +143,9 @@
             "dev": true
         },
         "node_modules/@types/vscode": {
-            "version": "1.63.1",
-            "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.63.1.tgz",
-            "integrity": "sha512-Z+ZqjRcnGfHP86dvx/BtSwWyZPKQ/LBdmAVImY82TphyjOw2KgTKcp7Nx92oNwCTsHzlshwexAG/WiY2JuUm3g==",
+            "version": "1.62.0",
+            "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.62.0.tgz",
+            "integrity": "sha512-iGlQJ1w5e3qPUryroO6v4lxg3ql1ztdTCwQW3xEwFawdyPLoeUSv48SYfMwc7kQA7h6ThUqflZIjgKAykeF9oA==",
             "dev": true
         },
         "node_modules/@typescript-eslint/eslint-plugin": {
@@ -4053,9 +4053,9 @@
             "dev": true
         },
         "@types/vscode": {
-            "version": "1.63.1",
-            "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.63.1.tgz",
-            "integrity": "sha512-Z+ZqjRcnGfHP86dvx/BtSwWyZPKQ/LBdmAVImY82TphyjOw2KgTKcp7Nx92oNwCTsHzlshwexAG/WiY2JuUm3g==",
+            "version": "1.62.0",
+            "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.62.0.tgz",
+            "integrity": "sha512-iGlQJ1w5e3qPUryroO6v4lxg3ql1ztdTCwQW3xEwFawdyPLoeUSv48SYfMwc7kQA7h6ThUqflZIjgKAykeF9oA==",
             "dev": true
         },
         "@typescript-eslint/eslint-plugin": {
diff --git a/editors/code/package.json b/editors/code/package.json
index 9ab57d7dc5..d70ac9aacb 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -21,7 +21,7 @@
         "Programming Languages"
     ],
     "engines": {
-        "vscode": "^1.63.0"
+        "vscode": "^1.62.0"
     },
     "enableProposedApi": true,
     "scripts": {
@@ -44,7 +44,7 @@
     },
     "devDependencies": {
         "@types/node": "~14.17.5",
-        "@types/vscode": "~1.63.0",
+        "@types/vscode": "~1.62.0",
         "@typescript-eslint/eslint-plugin": "^5.5.0",
         "@typescript-eslint/parser": "^5.5.0",
         "@vscode/test-electron": "^1.6.2",
diff --git a/editors/code/src/ast_inspector.ts b/editors/code/src/ast_inspector.ts
index 28d8b2a5fd..4fdd167bd5 100644
--- a/editors/code/src/ast_inspector.ts
+++ b/editors/code/src/ast_inspector.ts
@@ -59,7 +59,7 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
         }
     }
 
-    private onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]) {
+    private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) {
         if (!this.findAstTextEditor()) {
             this.setRustEditor(undefined);
             return;
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 9248bd1b6f..a21a742a57 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -125,7 +125,7 @@ export interface SsrParams {
     parseOnly: boolean;
     textDocument: lc.TextDocumentIdentifier;
     position: lc.Position;
-    selections: readonly lc.Range[];
+    selections: lc.Range[];
 }
 export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr');
 


Test patch:
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 19a0e2c261..842643cb97 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -2361,46 +2361,53 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {}
 fn test_hover_async_block_impl_trait_has_goto_type_action() {
     check_actions(
         r#"
-//- minicore: future
+//- /main.rs crate:main deps:core
+// we don't use minicore here so that this test doesn't randomly fail
+// when someone edits minicore
 struct S;
 fn foo() {
     let fo$0o = async { S };
 }
+//- /core.rs crate:core
+pub mod future {
+    #[lang = "future_trait"]
+    pub trait Future {}
+}
 "#,
         expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "core::future::Future",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        1,
-                                    ),
-                                    full_range: 276..458,
-                                    focus_range: 315..321,
-                                    name: "Future",
-                                    kind: Trait,
-                                    description: "pub trait Future",
-                                },
+            [
+                GoToType(
+                    [
+                        HoverGotoTypeData {
+                            mod_path: "core::future::Future",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    1,
+                                ),
+                                full_range: 21..69,
+                                focus_range: 60..66,
+                                name: "Future",
+                                kind: Trait,
+                                description: "pub trait Future",
                             },
-                            HoverGotoTypeData {
-                                mod_path: "test::S",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..9,
-                                    focus_range: 7..8,
-                                    name: "S",
-                                    kind: Struct,
-                                    description: "struct S",
-                                },
+                        },
+                        HoverGotoTypeData {
+                            mod_path: "main::S",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 0..110,
+                                focus_range: 108..109,
+                                name: "S",
+                                kind: Struct,
+                                description: "struct S",
                             },
-                        ],
-                    ),
-                ]
-            "#]],
+                        },
+                    ],
+                ),
+            ]
+        "#]],
     );
 }
 
diff --git a/crates/test_utils/src/minicore.rs b/crates/test_utils/src/minicore.rs
index 8801567fa5..5c63e27879 100644
--- a/crates/test_utils/src/minicore.rs
+++ b/crates/test_utils/src/minicore.rs
@@ -36,6 +36,7 @@
 //!     bool_impl: option, fn
 //!     add:
 //!     as_ref: sized
+//!     drop:
 
 pub mod marker {
     // region:sized
@@ -118,7 +119,6 @@ pub mod clone {
 }
 // endregion:clone
 
-
 pub mod convert {
     // region:from
     pub trait From<T>: Sized {
@@ -195,6 +195,13 @@ pub mod ops {
     };
     // endregion:deref
 
+    // region:drop
+    #[lang = "drop"]
+    pub trait Drop {
+        fn drop(&mut self);
+    }
+    // endregion:drop
+
     // region:index
     mod index {
         #[lang = "index"]
@@ -237,6 +244,12 @@ pub mod ops {
     pub use self::index::{Index, IndexMut};
     // endregion:index
 
+    // region:drop
+    pub mod mem {
+        pub fn drop<T>(_x: T) {}
+    }
+    // endregion:drop
+
     // region:range
     mod range {
         #[lang = "RangeFull"]
@@ -620,13 +633,15 @@ pub mod prelude {
             clone::Clone,                       // :clone
             cmp::{Eq, PartialEq},               // :eq
             cmp::{Ord, PartialOrd},             // :ord
-            convert::{From, Into},              // :from
             convert::AsRef,                     // :as_ref
+            convert::{From, Into},              // :from
             default::Default,                   // :default
             iter::{IntoIterator, Iterator},     // :iterator
             macros::builtin::derive,            // :derive
             marker::Copy,                       // :copy
             marker::Sized,                      // :sized
+            mem::drop,                          // :drop
+            ops::Drop,                          // :drop
             ops::{Fn, FnMut, FnOnce},           // :fn
             option::Option::{self, None, Some}, // :option
             result::Result::{self, Err, Ok},    // :result