Repository: rust-analyzer/rust-analyzer
Pull request #11002 (2021-12-13 16:16:59)

fix: Add support for v6 macro metadata format
CC https://github.com/rust-analyzer/rust-analyzer/issues/10894#issuecomment-991993824

bors r+


Patch:
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 75f6b02577..ed1b2f64fd 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -1164,7 +1164,8 @@ impl<'a> SemanticsScope<'a> {
     }
 
     /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type
-    pub fn visible_traits(&self) -> FxHashSet<TraitId> {
+    // FIXME: rename to visible_traits to not repeat scope?
+    pub fn traits_in_scope(&self) -> FxHashSet<TraitId> {
         let resolver = &self.resolver;
         resolver.traits_in_scope(self.db.upcast())
     }
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index 949744c01b..ee49732240 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -1,10 +1,9 @@
+use std::iter;
+
 use hir::Semantics;
-use ide_db::{
-    helpers::{insert_whitespace_into_node::insert_ws_into, pick_best_token},
-    RootDatabase,
-};
+use ide_db::{helpers::pick_best_token, RootDatabase};
 use itertools::Itertools;
-use syntax::{ast, ted, AstNode, SyntaxKind, SyntaxNode};
+use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T};
 
 use crate::FilePosition;
 
@@ -50,7 +49,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
             let expansions = sema.expand_derive_macro(&attr)?;
             Some(ExpandedMacro {
                 name: tt,
-                expansion: expansions.into_iter().map(insert_ws_into).join(""),
+                expansion: expansions.into_iter().map(insert_whitespaces).join(""),
             })
         } else {
             None
@@ -83,7 +82,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
     // FIXME:
     // macro expansion may lose all white space information
     // But we hope someday we can use ra_fmt for that
-    let expansion = insert_ws_into(expanded?).to_string();
+    let expansion = insert_whitespaces(expanded?);
     Some(ExpandedMacro { name: name.unwrap_or_else(|| "???".to_owned()), expansion })
 }
 
@@ -123,6 +122,84 @@ fn expand<T: AstNode>(
     Some(expanded)
 }
 
+// FIXME: It would also be cool to share logic here and in the mbe tests,
+// which are pretty unreadable at the moment.
+fn insert_whitespaces(syn: SyntaxNode) -> String {
+    use SyntaxKind::*;
+    let mut res = String::new();
+
+    let mut indent = 0;
+    let mut last: Option<SyntaxKind> = None;
+
+    for event in syn.preorder_with_tokens() {
+        let token = match event {
+            WalkEvent::Enter(NodeOrToken::Token(token)) => token,
+            WalkEvent::Leave(NodeOrToken::Node(node))
+                if matches!(node.kind(), ATTR | MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL) =>
+            {
+                res.push('\n');
+                res.extend(iter::repeat(" ").take(2 * indent));
+                continue;
+            }
+            _ => continue,
+        };
+        let is_next = |f: fn(SyntaxKind) -> bool, default| -> bool {
+            token.next_token().map(|it| f(it.kind())).unwrap_or(default)
+        };
+        let is_last =
+            |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) };
+
+        match token.kind() {
+            k if is_text(k) && is_next(|it| !it.is_punct(), true) => {
+                res.push_str(token.text());
+                res.push(' ');
+            }
+            L_CURLY if is_next(|it| it != R_CURLY, true) => {
+                indent += 1;
+                if is_last(is_text, false) {
+                    res.push(' ');
+                }
+                res.push_str("{\n");
+                res.extend(iter::repeat(" ").take(2 * indent));
+            }
+            R_CURLY if is_last(|it| it != L_CURLY, true) => {
+                indent = indent.saturating_sub(1);
+                res.push('\n');
+                res.extend(iter::repeat(" ").take(2 * indent));
+                res.push_str("}");
+            }
+            R_CURLY => {
+                res.push_str("}\n");
+                res.extend(iter::repeat(" ").take(2 * indent));
+            }
+            LIFETIME_IDENT if is_next(|it| it == IDENT || it == MUT_KW, true) => {
+                res.push_str(token.text());
+                res.push(' ');
+            }
+            AS_KW => {
+                res.push_str(token.text());
+                res.push(' ');
+            }
+            T![;] => {
+                res.push_str(";\n");
+                res.extend(iter::repeat(" ").take(2 * indent));
+            }
+            T![->] => res.push_str(" -> "),
+            T![=] => res.push_str(" = "),
+            T![=>] => res.push_str(" => "),
+            _ => res.push_str(token.text()),
+        }
+
+        last = Some(token.kind());
+    }
+
+    return res;
+
+    fn is_text(k: SyntaxKind) -> bool {
+        k.is_keyword() || k.is_literal() || k == IDENT
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use expect_test::{expect, Expect};
diff --git a/crates/ide_assists/src/handlers/add_missing_impl_members.rs b/crates/ide_assists/src/handlers/add_missing_impl_members.rs
index a10eca10d1..a145598c79 100644
--- a/crates/ide_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide_assists/src/handlers/add_missing_impl_members.rs
@@ -1,5 +1,5 @@
 use hir::HasSource;
-use ide_db::{helpers::insert_whitespace_into_node::insert_ws_into, traits::resolve_target_trait};
+use ide_db::traits::resolve_target_trait;
 use syntax::ast::{self, make, AstNode};
 
 use crate::{
@@ -105,7 +105,7 @@ fn add_missing_impl_members_inner(
     let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
 
     let missing_items = filter_assoc_items(
-        &ctx.sema,
+        ctx.db(),
         &ide_db::traits::get_missing_assoc_items(&ctx.sema, &impl_def),
         mode,
     );
@@ -117,17 +117,6 @@ fn add_missing_impl_members_inner(
     let target = impl_def.syntax().text_range();
     acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
         let target_scope = ctx.sema.scope(impl_def.syntax());
-        let missing_items = missing_items
-            .into_iter()
-            .map(|it| {
-                if ctx.sema.hir_file_for(it.syntax()).is_macro() {
-                    if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) {
-                        return it;
-                    }
-                }
-                it.clone_for_update()
-            })
-            .collect();
         let (new_impl_def, first_new_item) = add_trait_assoc_items_to_impl(
             &ctx.sema,
             missing_items,
@@ -901,44 +890,6 @@ impl Default for Foo {
         Self(Default::default())
     }
 }
-"#,
-        )
-    }
-
-    #[test]
-    fn test_from_macro() {
-        check_assist(
-            add_missing_default_members,
-            r#"
-macro_rules! foo {
-    () => {
-        trait FooB {
-            fn foo<'lt>(&'lt self) {}
-        }
-    }
-}
-foo!();
-struct Foo(usize);
-
-impl FooB for Foo {
-    $0
-}
-"#,
-            r#"
-macro_rules! foo {
-    () => {
-        trait FooB {
-            fn foo<'lt>(&'lt self) {}
-        }
-    }
-}
-foo!();
-struct Foo(usize);
-
-impl FooB for Foo {
-    $0fn foo< 'lt>(& 'lt self){}
-
-}
 "#,
         )
     }
diff --git a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
index 7fbbdb4f5e..eca6d047a1 100644
--- a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -148,7 +148,7 @@ fn is_ref_and_impls_iter_method(
     let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted();
     let scope = sema.scope(iterable.syntax());
     let krate = scope.module()?.krate();
-    let traits_in_scope = scope.visible_traits();
+    let traits_in_scope = scope.traits_in_scope();
     let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
 
     let has_wanted_method = ty
diff --git a/crates/ide_assists/src/handlers/generate_delegate_methods.rs b/crates/ide_assists/src/handlers/generate_delegate_methods.rs
index 31b438b15e..090dab946a 100644
--- a/crates/ide_assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide_assists/src/handlers/generate_delegate_methods.rs
@@ -47,18 +47,18 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext)
     let strukt_name = strukt.name()?;
     let current_module = ctx.sema.scope(strukt.syntax()).module()?;
 
-    let (field_name, field_ty, target) = match ctx.find_node_at_offset::<ast::RecordField>() {
+    let (field_name, field_ty) = match ctx.find_node_at_offset::<ast::RecordField>() {
         Some(field) => {
             let field_name = field.name()?;
             let field_ty = field.ty()?;
-            (format!("{}", field_name), field_ty, field.syntax().text_range())
+            (format!("{}", field_name), field_ty)
         }
         None => {
             let field = ctx.find_node_at_offset::<ast::TupleField>()?;
             let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
             let field_list_index = field_list.fields().position(|it| it == field)?;
             let field_ty = field.ty()?;
-            (format!("{}", field_list_index), field_ty, field.syntax().text_range())
+            (format!("{}", field_list_index), field_ty)
         }
     };
 
@@ -74,6 +74,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext)
         Option::<()>::None
     });
 
+    let target = field_ty.syntax().text_range();
     for method in methods {
         let adt = ast::Adt::Struct(strukt.clone());
         let name = method.name(ctx.db()).to_string();
diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
index 15025cf0d0..d831289775 100644
--- a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
+++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
@@ -92,7 +92,7 @@ fn get_impl_method(
     let scope = ctx.sema.scope(impl_.syntax());
     let krate = impl_def.module(db).krate();
     let ty = impl_def.self_ty(db);
-    let traits_in_scope = scope.visible_traits();
+    let traits_in_scope = scope.traits_in_scope();
     ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func))
 }
 
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index b3723710a8..46bf4d7b46 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -1,5 +1,4 @@
 use hir::ModuleDef;
-use ide_db::helpers::insert_whitespace_into_node::insert_ws_into;
 use ide_db::helpers::{
     get_path_at_cursor_in_tt, import_assets::NameToImport, mod_path_to_ast,
     parse_tt_as_comma_sep_paths,
@@ -171,7 +170,7 @@ fn impl_def_from_trait(
 ) -> Option<(ast::Impl, ast::AssocItem)> {
     let trait_ = trait_?;
     let target_scope = sema.scope(annotated_name.syntax());
-    let trait_items = filter_assoc_items(sema, &trait_.items(sema.db), DefaultMethods::No);
+    let trait_items = filter_assoc_items(sema.db, &trait_.items(sema.db), DefaultMethods::No);
     if trait_items.is_empty() {
         return None;
     }
@@ -194,17 +193,6 @@ fn impl_def_from_trait(
         node
     };
 
-    let trait_items = trait_items
-        .into_iter()
-        .map(|it| {
-            if sema.hir_file_for(it.syntax()).is_macro() {
-                if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) {
-                    return it;
-                }
-            }
-            it.clone_for_update()
-        })
-        .collect();
     let (impl_def, first_assoc_item) =
         add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
 
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index a1dfa065ff..61ac45637b 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -217,6 +217,8 @@ mod handlers {
             generate_constant::generate_constant,
             generate_default_from_enum_variant::generate_default_from_enum_variant,
             generate_default_from_new::generate_default_from_new,
+            generate_delegate_methods::generate_delegate_methods,
+            generate_deref::generate_deref,
             generate_derive::generate_derive,
             generate_documentation_template::generate_documentation_template,
             generate_enum_is_method::generate_enum_is_method,
@@ -285,8 +287,6 @@ mod handlers {
             generate_getter::generate_getter,
             generate_getter::generate_getter_mut,
             generate_setter::generate_setter,
-            generate_delegate_methods::generate_delegate_methods,
-            generate_deref::generate_deref,
             // Are you sure you want to add new assist here, and not to the
             // sorted list above?
         ]
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 90ec710c8e..03433fc42a 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -5,7 +5,7 @@ use std::ops;
 use itertools::Itertools;
 
 pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
-use hir::{db::HirDatabase, HirDisplay, Semantics};
+use hir::{db::HirDatabase, HasSource, HirDisplay};
 use ide_db::{
     helpers::FamousDefs, helpers::SnippetCap, path_transform::PathTransform, RootDatabase,
 };
@@ -92,7 +92,7 @@ pub enum DefaultMethods {
 }
 
 pub fn filter_assoc_items(
-    sema: &Semantics<RootDatabase>,
+    db: &RootDatabase,
     items: &[hir::AssocItem],
     default_methods: DefaultMethods,
 ) -> Vec<ast::AssocItem> {
@@ -109,11 +109,11 @@ pub fn filter_assoc_items(
     items
         .iter()
         // Note: This throws away items with no source.
-        .filter_map(|&i| {
+        .filter_map(|i| {
             let item = match i {
-                hir::AssocItem::Function(i) => ast::AssocItem::Fn(sema.source(i)?.value),
-                hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(sema.source(i)?.value),
-                hir::AssocItem::Const(i) => ast::AssocItem::Const(sema.source(i)?.value),
+                hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db)?.value),
+                hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db)?.value),
+                hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db)?.value),
             };
             Some(item)
         })
@@ -129,7 +129,7 @@ pub fn filter_assoc_items(
 }
 
 pub fn add_trait_assoc_items_to_impl(
-    sema: &Semantics<RootDatabase>,
+    sema: &hir::Semantics<ide_db::RootDatabase>,
     items: Vec<ast::AssocItem>,
     trait_: hir::Trait,
     impl_: ast::Impl,
@@ -140,6 +140,7 @@ pub fn add_trait_assoc_items_to_impl(
     let transform = PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
 
     let items = items.into_iter().map(|assoc_item| {
+        let assoc_item = assoc_item.clone_for_update();
         transform.apply(assoc_item.syntax());
         assoc_item.remove_attrs_and_docs();
         assoc_item
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index e7371270fb..e01e9c9fa7 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -79,7 +79,7 @@ fn complete_methods(
 ) {
     if let Some(krate) = ctx.krate {
         let mut seen_methods = FxHashSet::default();
-        let traits_in_scope = ctx.scope.visible_traits();
+        let traits_in_scope = ctx.scope.traits_in_scope();
         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);
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index a0d6d5cdc6..b5c3d83c16 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -187,7 +187,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
 
             let krate = ctx.krate;
             if let Some(krate) = krate {
-                let traits_in_scope = ctx.scope.visible_traits();
+                let traits_in_scope = ctx.scope.traits_in_scope();
                 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
                     add_assoc_item(acc, ctx, item);
                     None::<()>
@@ -220,7 +220,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                     add_enum_variants(acc, ctx, e);
                 }
 
-                let traits_in_scope = ctx.scope.visible_traits();
+                let traits_in_scope = ctx.scope.traits_in_scope();
                 let mut seen = FxHashSet::default();
                 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
                     // We might iterate candidates of a trait multiple times here, so deduplicate
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 18a5f64891..7e78b7136c 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -4,7 +4,6 @@ pub mod generated_lints;
 pub mod import_assets;
 pub mod insert_use;
 pub mod merge_imports;
-pub mod insert_whitespace_into_node;
 pub mod node_ext;
 pub mod rust_doc;
 
diff --git a/crates/ide_db/src/helpers/insert_whitespace_into_node.rs b/crates/ide_db/src/helpers/insert_whitespace_into_node.rs
deleted file mode 100644
index 251a4caa13..0000000000
--- a/crates/ide_db/src/helpers/insert_whitespace_into_node.rs
+++ /dev/null
@@ -1,119 +0,0 @@
-//! Utilities for formatting macro expanded nodes until we get a proper formatter.
-use syntax::{
-    ast::make,
-    ted::{self, Position},
-    NodeOrToken,
-    SyntaxKind::{self, *},
-    SyntaxNode, SyntaxToken, WalkEvent, T,
-};
-
-// FIXME: It would also be cool to share logic here and in the mbe tests,
-// which are pretty unreadable at the moment.
-/// Renders a [`SyntaxNode`] with whitespace inserted between tokens that require them.
-pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode {
-    let mut indent = 0;
-    let mut last: Option<SyntaxKind> = None;
-    let mut mods = Vec::new();
-    let syn = syn.clone_subtree().clone_for_update();
-
-    let before = Position::before;
-    let after = Position::after;
-
-    let do_indent = |pos: fn(_) -> Position, token: &SyntaxToken, indent| {
-        (pos(token.clone()), make::tokens::whitespace(&" ".repeat(2 * indent)))
-    };
-    let do_ws = |pos: fn(_) -> Position, token: &SyntaxToken| {
-        (pos(token.clone()), make::tokens::single_space())
-    };
-    let do_nl = |pos: fn(_) -> Position, token: &SyntaxToken| {
-        (pos(token.clone()), make::tokens::single_newline())
-    };
-
-    for event in syn.preorder_with_tokens() {
-        let token = match event {
-            WalkEvent::Enter(NodeOrToken::Token(token)) => token,
-            WalkEvent::Leave(NodeOrToken::Node(node))
-                if matches!(node.kind(), ATTR | MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL) =>
-            {
-                if indent > 0 {
-                    mods.push((
-                        Position::after(node.clone()),
-                        make::tokens::whitespace(&" ".repeat(2 * indent)),
-                    ));
-                }
-                if node.parent().is_some() {
-                    mods.push((Position::after(node), make::tokens::single_newline()));
-                }
-                continue;
-            }
-            _ => continue,
-        };
-        let tok = &token;
-
-        let is_next = |f: fn(SyntaxKind) -> bool, default| -> bool {
-            tok.next_token().map(|it| f(it.kind())).unwrap_or(default)
-        };
-        let is_last =
-            |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) };
-
-        match tok.kind() {
-            k if is_text(k) && is_next(|it| !it.is_punct(), true) => {
-                mods.push(do_ws(after, tok));
-            }
-            L_CURLY if is_next(|it| it != R_CURLY, true) => {
-                indent += 1;
-                if is_last(is_text, false) {
-                    mods.push(do_ws(before, tok));
-                }
-
-                if indent > 0 {
-                    mods.push(do_indent(after, tok, indent));
-                }
-                mods.push(do_nl(after, &tok));
-            }
-            R_CURLY if is_last(|it| it != L_CURLY, true) => {
-                indent = indent.saturating_sub(1);
-
-                if indent > 0 {
-                    mods.push(do_indent(before, tok, indent));
-                }
-                mods.push(do_nl(before, tok));
-            }
-            R_CURLY => {
-                if indent > 0 {
-                    mods.push(do_indent(after, tok, indent));
-                }
-                mods.push(do_nl(after, tok));
-            }
-            LIFETIME_IDENT if is_next(|it| is_text(it), true) => {
-                mods.push(do_ws(after, tok));
-            }
-            AS_KW => {
-                mods.push(do_ws(after, tok));
-            }
-            T![;] => {
-                if indent > 0 {
-                    mods.push(do_indent(after, tok, indent));
-                }
-                mods.push(do_nl(after, tok));
-            }
-            T![->] | T![=] | T![=>] => {
-                mods.push(do_ws(before, tok));
-                mods.push(do_ws(after, tok));
-            }
-            _ => (),
-        }
-
-        last = Some(tok.kind());
-    }
-
-    for (pos, insert) in mods {
-        ted::insert(pos, insert);
-    }
-
-    syn
-}
-
-fn is_text(k: SyntaxKind) -> bool {
-    k.is_keyword() || k.is_literal() || k == IDENT
-}
diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs
index 7902295d29..84e5f82604 100644
--- a/crates/ide_ssr/src/resolving.rs
+++ b/crates/ide_ssr/src/resolving.rs
@@ -222,7 +222,7 @@ impl<'db> ResolutionScope<'db> {
             adt.ty(self.scope.db).iterate_path_candidates(
                 self.scope.db,
                 self.scope.module()?.krate(),
-                &self.scope.visible_traits(),
+                &self.scope.traits_in_scope(),
                 None,
                 |_ty, assoc_item| {
                     let item_name = assoc_item.name(self.scope.db)?;
diff --git a/crates/proc_macro_api/src/version.rs b/crates/proc_macro_api/src/version.rs
index 4c065c93c4..f29970d477 100644
--- a/crates/proc_macro_api/src/version.rs
+++ b/crates/proc_macro_api/src/version.rs
@@ -99,7 +99,7 @@ fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&'
 /// * [length byte] next 1 byte tells us how many bytes we should read next
 ///   for the version string's utf8 bytes
 /// * [version string bytes encoded in utf8] <- GET THIS BOI
-/// * [some more bytes that we don really care but still there] :-)
+/// * [some more bytes that we don't really care but about still there] :-)
 /// Check this issue for more about the bytes layout:
 /// <https://github.com/rust-analyzer/rust-analyzer/issues/6174>
 fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
@@ -108,15 +108,25 @@ fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
 
     let dot_rustc = read_section(&dylib_mmaped, ".rustc")?;
 
-    let header = &dot_rustc[..8];
-    const EXPECTED_HEADER: [u8; 8] = [b'r', b'u', b's', b't', 0, 0, 0, 5];
-    // check if header is valid
-    if header != EXPECTED_HEADER {
+    // check if magic is valid
+    if &dot_rustc[0..4] != b"rust" {
         return Err(io::Error::new(
             io::ErrorKind::InvalidData,
-            format!("only metadata version 5 is supported, section header was: {:?}", header),
+            format!("unknown metadata magic, expected `rust`, found `{:?}`", &dot_rustc[0..4]),
         ));
     }
+    let version = u32::from_be_bytes([dot_rustc[4], dot_rustc[5], dot_rustc[6], dot_rustc[7]]);
+    // Last supported version is:
+    // https://github.com/rust-lang/rust/commit/0696e79f2740ad89309269b460579e548a5cd632
+    match version {
+        5 | 6 => {}
+        _ => {
+            return Err(io::Error::new(
+                io::ErrorKind::InvalidData,
+                format!("unsupported metadata version {}", version),
+            ));
+        }
+    }
 
     let snappy_portion = &dot_rustc[8..];
 
@@ -130,7 +140,7 @@ fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
     // to know the length
     let mut bytes_before_version = [0u8; 13];
     snappy_decoder.read_exact(&mut bytes_before_version)?;
-    let length = bytes_before_version[12]; // what? can't use -1 indexing?
+    let length = bytes_before_version[12];
 
     let mut version_string_utf8 = vec![0u8; length as usize];
     snappy_decoder.read_exact(&mut version_string_utf8)?;


Test patch:
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index 08947e4ecd..5a79979533 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -222,10 +222,10 @@ fn assist_order_field_struct() {
     let mut assists = assists.iter();
 
     assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
+    assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`");
     assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
     assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
     assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
-    assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`");
     assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
 }