Repository: rust-analyzer/rust-analyzer
Pull request #11210 (2022-01-06 12:32:54)

feat: Deprioritize ops methods in completion
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/10593



Veykril: This currently slows down completions a lot because the lang_item queries aren't cheap and I think the cached values get lost pretty fast, at least the integrated completion benchmark is hitting it twice, even though the second time should be cached.

So I guess we want a better query for fetching the ops traits?
Veykril: Tried wrapping the lang item queries in an extra query which reduces the performance issues a bit, but still adds 7ms in the case where the query doesnt get reexecuted due to validation checking I suppose(hprof adds a `7ms ???` span). Any ideas on how to do this in a different way maybe? 
flodiebold: I think you could just look for the `lang` attribute on the trait, instead of collecting all the lang items and checking whether it's one of them?

Patch:
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index b0ff797275..2c91a11e15 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1610,6 +1610,12 @@ pub struct Trait {
 }
 
 impl Trait {
+    pub fn lang(db: &dyn HirDatabase, krate: Crate, name: &Name) -> Option<Trait> {
+        db.lang_item(krate.into(), name.to_smol_str())
+            .and_then(LangItemTarget::as_trait)
+            .map(Into::into)
+    }
+
     pub fn module(self, db: &dyn HirDatabase) -> Module {
         Module { id: self.id.lookup(db.upcast()).container }
     }
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 5061861239..f9c6564d01 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -242,7 +242,7 @@ impl SourceToDefCtx<'_, '_> {
 
     pub(super) fn item_to_macro_call(&mut self, src: InFile<ast::Item>) -> Option<MacroCallId> {
         let map = self.dyn_map(src.as_ref())?;
-        map[keys::ATTR_MACRO_CALL].get(&src.value).copied()
+        map[keys::ATTR_MACRO_CALL].get(&src).copied()
     }
 
     pub(super) fn attr_to_derive_macro_call(
@@ -251,7 +251,7 @@ impl SourceToDefCtx<'_, '_> {
         src: InFile<ast::Attr>,
     ) -> Option<(AttrId, &[Option<MacroCallId>])> {
         let map = self.dyn_map(item)?;
-        map[keys::DERIVE_MACRO_CALL].get(&src.value).map(|(id, ids)| (*id, &**ids))
+        map[keys::DERIVE_MACRO_CALL].get(&src).map(|(id, ids)| (*id, &**ids))
     }
 
     fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
@@ -259,7 +259,7 @@ impl SourceToDefCtx<'_, '_> {
         src: InFile<Ast>,
         key: Key<Ast, ID>,
     ) -> Option<ID> {
-        self.dyn_map(src.as_ref())?[key].get(&src.value).copied()
+        self.dyn_map(src.as_ref())?[key].get(&src).copied()
     }
 
     fn dyn_map<Ast: AstNode + 'static>(&mut self, src: InFile<&Ast>) -> Option<&DynMap> {
@@ -277,7 +277,7 @@ impl SourceToDefCtx<'_, '_> {
     pub(super) fn type_param_to_def(&mut self, src: InFile<ast::TypeParam>) -> Option<TypeParamId> {
         let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into();
         let dyn_map = self.cache_for(container, src.file_id);
-        dyn_map[keys::TYPE_PARAM].get(&src.value).copied()
+        dyn_map[keys::TYPE_PARAM].get(&src).copied()
     }
 
     pub(super) fn lifetime_param_to_def(
@@ -286,7 +286,7 @@ impl SourceToDefCtx<'_, '_> {
     ) -> Option<LifetimeParamId> {
         let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into();
         let dyn_map = self.cache_for(container, src.file_id);
-        dyn_map[keys::LIFETIME_PARAM].get(&src.value).copied()
+        dyn_map[keys::LIFETIME_PARAM].get(&src).copied()
     }
 
     pub(super) fn const_param_to_def(
@@ -295,7 +295,7 @@ impl SourceToDefCtx<'_, '_> {
     ) -> Option<ConstParamId> {
         let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into();
         let dyn_map = self.cache_for(container, src.file_id);
-        dyn_map[keys::CONST_PARAM].get(&src.value).copied()
+        dyn_map[keys::CONST_PARAM].get(&src).copied()
     }
 
     pub(super) fn generic_param_to_def(
@@ -316,9 +316,10 @@ impl SourceToDefCtx<'_, '_> {
     }
 
     pub(super) fn macro_to_def(&mut self, src: InFile<ast::Macro>) -> Option<MacroDefId> {
-        let makro = self.dyn_map(src.as_ref()).and_then(|it| it[keys::MACRO].get(&src.value));
-        if let Some(&makro) = makro {
-            return Some(makro);
+        let makro =
+            self.dyn_map(src.as_ref()).and_then(|it| it[keys::MACRO_CALL].get(&src).copied());
+        if let res @ Some(_) = makro {
+            return res;
         }
 
         // Not all macros are recorded in the dyn map, only the ones behaving like items, so fall back
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index a244d32149..c60ef87a93 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -255,6 +255,10 @@ impl Attrs {
         }
     }
 
+    pub fn lang(&self) -> Option<&SmolStr> {
+        self.by_key("lang").string_value()
+    }
+
     pub fn docs(&self) -> Option<Documentation> {
         let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_deref()? {
             AttrInput::Literal(s) => Some(s),
@@ -390,9 +394,7 @@ impl AttrsWithOwner {
                         if let InFile { file_id, value: ModuleSource::SourceFile(file) } =
                             mod_data.definition_source(db)
                         {
-                            map.append_module_inline_attrs(AttrSourceMap::new(InFile::new(
-                                file_id, &file,
-                            )));
+                            map.merge(AttrSourceMap::new(InFile::new(file_id, &file)));
                         }
                         return map;
                     }
@@ -554,11 +556,6 @@ fn inner_attributes(
 pub struct AttrSourceMap {
     source: Vec<Either<ast::Attr, ast::Comment>>,
     file_id: HirFileId,
-    /// If this map is for a module, this will be the [`HirFileId`] of the module's definition site,
-    /// while `file_id` will be the one of the module declaration site.
-    /// The usize is the index into `source` from which point on the entries reside in the def site
-    /// file.
-    mod_def_site_file_id: Option<(HirFileId, usize)>,
 }
 
 impl AttrSourceMap {
@@ -566,19 +563,11 @@ impl AttrSourceMap {
         Self {
             source: collect_attrs(owner.value).map(|(_, it)| it).collect(),
             file_id: owner.file_id,
-            mod_def_site_file_id: None,
         }
     }
 
-    /// Append a second source map to this one, this is required for modules, whose outline and inline
-    /// attributes can reside in different files
-    fn append_module_inline_attrs(&mut self, other: Self) {
-        assert!(self.mod_def_site_file_id.is_none() && other.mod_def_site_file_id.is_none());
-        let len = self.source.len();
+    fn merge(&mut self, other: Self) {
         self.source.extend(other.source);
-        if other.file_id != self.file_id {
-            self.mod_def_site_file_id = Some((other.file_id, len));
-        }
     }
 
     /// Maps the lowered `Attr` back to its original syntax node.
@@ -592,15 +581,9 @@ impl AttrSourceMap {
     }
 
     fn source_of_id(&self, id: AttrId) -> InFile<&Either<ast::Attr, ast::Comment>> {
-        let ast_idx = id.ast_index as usize;
-        let file_id = match self.mod_def_site_file_id {
-            Some((file_id, def_site_cut)) if def_site_cut <= ast_idx => file_id,
-            _ => self.file_id,
-        };
-
         self.source
-            .get(ast_idx)
-            .map(|it| InFile::new(file_id, it))
+            .get(id.ast_index as usize)
+            .map(|it| InFile::new(self.file_id, it))
             .unwrap_or_else(|| panic!("cannot find attr at index {:?}", id))
     }
 }
@@ -775,20 +758,20 @@ impl Attr {
 }
 
 #[derive(Debug, Clone, Copy)]
-pub struct AttrQuery<'a> {
-    attrs: &'a Attrs,
+pub struct AttrQuery<'attr> {
+    attrs: &'attr Attrs,
     key: &'static str,
 }
 
-impl<'a> AttrQuery<'a> {
-    pub fn tt_values(self) -> impl Iterator<Item = &'a Subtree> {
+impl<'attr> AttrQuery<'attr> {
+    pub fn tt_values(self) -> impl Iterator<Item = &'attr Subtree> {
         self.attrs().filter_map(|attr| match attr.input.as_deref()? {
             AttrInput::TokenTree(it, _) => Some(it),
             _ => None,
         })
     }
 
-    pub fn string_value(self) -> Option<&'a SmolStr> {
+    pub fn string_value(self) -> Option<&'attr SmolStr> {
         self.attrs().find_map(|attr| match attr.input.as_deref()? {
             AttrInput::Literal(it) => Some(it),
             _ => None,
@@ -799,7 +782,7 @@ impl<'a> AttrQuery<'a> {
         self.attrs().next().is_some()
     }
 
-    pub fn attrs(self) -> impl Iterator<Item = &'a Attr> + Clone {
+    pub fn attrs(self) -> impl Iterator<Item = &'attr Attr> + Clone {
         let key = self.key;
         self.attrs
             .iter()
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs
index 6b4abd8c44..d314b7fc01 100644
--- a/crates/hir_def/src/child_by_source.rs
+++ b/crates/hir_def/src/child_by_source.rs
@@ -30,50 +30,62 @@ pub trait ChildBySource {
 impl ChildBySource for TraitId {
     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
         let data = db.trait_data(*self);
-
-        data.attribute_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each(
-            |(ast_id, call_id)| {
-                res[keys::ATTR_MACRO_CALL].insert(ast_id.to_node(db.upcast()), call_id);
-            },
-        );
-        data.items.iter().for_each(|&(_, item)| {
-            add_assoc_item(db, res, file_id, item);
-        });
+        // FIXME attribute macros
+        for (_name, item) in data.items.iter() {
+            match *item {
+                AssocItemId::FunctionId(func) => {
+                    let loc = func.lookup(db);
+                    if loc.id.file_id() == file_id {
+                        let src = loc.source(db);
+                        res[keys::FUNCTION].insert(src, func)
+                    }
+                }
+                AssocItemId::ConstId(konst) => {
+                    let loc = konst.lookup(db);
+                    if loc.id.file_id() == file_id {
+                        let src = loc.source(db);
+                        res[keys::CONST].insert(src, konst)
+                    }
+                }
+                AssocItemId::TypeAliasId(ty) => {
+                    let loc = ty.lookup(db);
+                    if loc.id.file_id() == file_id {
+                        let src = loc.source(db);
+                        res[keys::TYPE_ALIAS].insert(src, ty)
+                    }
+                }
+            }
+        }
     }
 }
 
 impl ChildBySource for ImplId {
     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
         let data = db.impl_data(*self);
-        data.attribute_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each(
-            |(ast_id, call_id)| {
-                res[keys::ATTR_MACRO_CALL].insert(ast_id.to_node(db.upcast()), call_id);
-            },
-        );
-        data.items.iter().for_each(|&item| {
-            add_assoc_item(db, res, file_id, item);
-        });
-    }
-}
-
-fn add_assoc_item(db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId, item: AssocItemId) {
-    match item {
-        AssocItemId::FunctionId(func) => {
-            let loc = func.lookup(db);
-            if loc.id.file_id() == file_id {
-                res[keys::FUNCTION].insert(loc.source(db).value, func)
-            }
-        }
-        AssocItemId::ConstId(konst) => {
-            let loc = konst.lookup(db);
-            if loc.id.file_id() == file_id {
-                res[keys::CONST].insert(loc.source(db).value, konst)
-            }
-        }
-        AssocItemId::TypeAliasId(ty) => {
-            let loc = ty.lookup(db);
-            if loc.id.file_id() == file_id {
-                res[keys::TYPE_ALIAS].insert(loc.source(db).value, ty)
+        // FIXME attribute macros
+        for &item in data.items.iter() {
+            match item {
+                AssocItemId::FunctionId(func) => {
+                    let loc = func.lookup(db);
+                    if loc.id.file_id() == file_id {
+                        let src = loc.source(db);
+                        res[keys::FUNCTION].insert(src, func)
+                    }
+                }
+                AssocItemId::ConstId(konst) => {
+                    let loc = konst.lookup(db);
+                    if loc.id.file_id() == file_id {
+                        let src = loc.source(db);
+                        res[keys::CONST].insert(src, konst)
+                    }
+                }
+                AssocItemId::TypeAliasId(ty) => {
+                    let loc = ty.lookup(db);
+                    if loc.id.file_id() == file_id {
+                        let src = loc.source(db);
+                        res[keys::TYPE_ALIAS].insert(src, ty)
+                    }
+                }
             }
         }
     }
@@ -89,75 +101,112 @@ impl ChildBySource for ModuleId {
 
 impl ChildBySource for ItemScope {
     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
-        self.declarations().for_each(|item| add_module_def(db, res, file_id, item));
-        self.impls().for_each(|imp| add_impl(db, res, file_id, imp));
-        self.unnamed_consts().for_each(|konst| {
-            let loc = konst.lookup(db);
-            if loc.id.file_id() == file_id {
-                res[keys::CONST].insert(loc.source(db).value, konst);
-            }
-        });
+        self.declarations().for_each(|item| add_module_def(db, file_id, res, item));
         self.macros().for_each(|(_, makro)| {
             let ast_id = makro.ast_id();
             if ast_id.either(|it| it.file_id, |it| it.file_id) == file_id {
                 let src = match ast_id {
-                    Either::Left(ast_id) => ast_id.to_node(db.upcast()),
+                    Either::Left(ast_id) => ast_id.with_value(ast_id.to_node(db.upcast())),
                     // FIXME: Do we need to add proc-macros into a PROCMACRO dynmap here?
                     Either::Right(_fn) => return,
                 };
-                res[keys::MACRO].insert(src, makro);
+                res[keys::MACRO_CALL].insert(src, makro);
+            }
+        });
+        self.unnamed_consts().for_each(|konst| {
+            let src = konst.lookup(db).source(db);
+            res[keys::CONST].insert(src, konst);
+        });
+        self.impls().for_each(|imp| add_impl(db, file_id, res, imp));
+        self.attr_macro_invocs().for_each(|(ast_id, call_id)| {
+            let item = ast_id.with_value(ast_id.to_node(db.upcast()));
+            res[keys::ATTR_MACRO_CALL].insert(item, call_id);
+        });
+        self.derive_macro_invocs().for_each(|(ast_id, calls)| {
+            let adt = ast_id.to_node(db.upcast());
+            for (attr_id, calls) in calls {
+                if let Some(Either::Right(attr)) =
+                    adt.doc_comments_and_attrs().nth(attr_id.ast_index as usize)
+                {
+                    res[keys::DERIVE_MACRO_CALL]
+                        .insert(ast_id.with_value(attr), (attr_id, calls.into()));
+                }
             }
         });
-        self.attr_macro_invocs().filter(|(id, _)| id.file_id == file_id).for_each(
-            |(ast_id, call_id)| {
-                res[keys::ATTR_MACRO_CALL].insert(ast_id.to_node(db.upcast()), call_id);
-            },
-        );
-        self.derive_macro_invocs().filter(|(id, _)| id.file_id == file_id).for_each(
-            |(ast_id, calls)| {
-                let adt = ast_id.to_node(db.upcast());
-                calls.for_each(|(attr_id, calls)| {
-                    if let Some(Either::Right(attr)) =
-                        adt.doc_comments_and_attrs().nth(attr_id.ast_index as usize)
-                    {
-                        res[keys::DERIVE_MACRO_CALL].insert(attr, (attr_id, calls.into()));
-                    }
-                });
-            },
-        );
 
         fn add_module_def(
             db: &dyn DefDatabase,
-            map: &mut DynMap,
             file_id: HirFileId,
+            map: &mut DynMap,
             item: ModuleDefId,
         ) {
-            macro_rules! insert {
-                ($map:ident[$key:path].$insert:ident($id:ident)) => {{
-                    let loc = $id.lookup(db);
+            match item {
+                ModuleDefId::FunctionId(func) => {
+                    let loc = func.lookup(db);
                     if loc.id.file_id() == file_id {
-                        $map[$key].$insert(loc.source(db).value, $id)
+                        let src = loc.source(db);
+                        map[keys::FUNCTION].insert(src, func)
                     }
-                }};
-            }
-            match item {
-                ModuleDefId::FunctionId(id) => insert!(map[keys::FUNCTION].insert(id)),
-                ModuleDefId::ConstId(id) => insert!(map[keys::CONST].insert(id)),
-                ModuleDefId::StaticId(id) => insert!(map[keys::STATIC].insert(id)),
-                ModuleDefId::TypeAliasId(id) => insert!(map[keys::TYPE_ALIAS].insert(id)),
-                ModuleDefId::TraitId(id) => insert!(map[keys::TRAIT].insert(id)),
+                }
+                ModuleDefId::ConstId(konst) => {
+                    let loc = konst.lookup(db);
+                    if loc.id.file_id() == file_id {
+                        let src = loc.source(db);
+                        map[keys::CONST].insert(src, konst)
+                    }
+                }
+                ModuleDefId::StaticId(statik) => {
+                    let loc = statik.lookup(db);
+                    if loc.id.file_id() == file_id {
+                        let src = loc.source(db);
+                        map[keys::STATIC].insert(src, statik)
+                    }
+                }
+                ModuleDefId::TypeAliasId(ty) => {
+                    let loc = ty.lookup(db);
+                    if loc.id.file_id() == file_id {
+                        let src = loc.source(db);
+                        map[keys::TYPE_ALIAS].insert(src, ty)
+                    }
+                }
+                ModuleDefId::TraitId(trait_) => {
+                    let loc = trait_.lookup(db);
+                    if loc.id.file_id() == file_id {
+                        let src = loc.source(db);
+                        map[keys::TRAIT].insert(src, trait_)
+                    }
+                }
                 ModuleDefId::AdtId(adt) => match adt {
-                    AdtId::StructId(id) => insert!(map[keys::STRUCT].insert(id)),
-                    AdtId::UnionId(id) => insert!(map[keys::UNION].insert(id)),
-                    AdtId::EnumId(id) => insert!(map[keys::ENUM].insert(id)),
+                    AdtId::StructId(strukt) => {
+                        let loc = strukt.lookup(db);
+                        if loc.id.file_id() == file_id {
+                            let src = loc.source(db);
+                            map[keys::STRUCT].insert(src, strukt)
+                        }
+                    }
+                    AdtId::UnionId(union_) => {
+                        let loc = union_.lookup(db);
+                        if loc.id.file_id() == file_id {
+                            let src = loc.source(db);
+                            map[keys::UNION].insert(src, union_)
+                        }
+                    }
+                    AdtId::EnumId(enum_) => {
+                        let loc = enum_.lookup(db);
+                        if loc.id.file_id() == file_id {
+                            let src = loc.source(db);
+                            map[keys::ENUM].insert(src, enum_)
+                        }
+                    }
                 },
                 _ => (),
             }
         }
-        fn add_impl(db: &dyn DefDatabase, map: &mut DynMap, file_id: HirFileId, imp: ImplId) {
+        fn add_impl(db: &dyn DefDatabase, file_id: HirFileId, map: &mut DynMap, imp: ImplId) {
             let loc = imp.lookup(db);
             if loc.id.file_id() == file_id {
-                map[keys::IMPL].insert(loc.source(db).value, imp)
+                let src = loc.source(db);
+                map[keys::IMPL].insert(src, imp)
             }
         }
     }
@@ -171,8 +220,12 @@ impl ChildBySource for VariantId {
         for (local_id, source) in arena_map.value.iter() {
             let id = FieldId { parent, local_id };
             match source.clone() {
-                Either::Left(source) => res[keys::TUPLE_FIELD].insert(source, id),
-                Either::Right(source) => res[keys::RECORD_FIELD].insert(source, id),
+                Either::Left(source) => {
+                    res[keys::TUPLE_FIELD].insert(arena_map.with_value(source), id)
+                }
+                Either::Right(source) => {
+                    res[keys::RECORD_FIELD].insert(arena_map.with_value(source), id)
+                }
             }
         }
     }
@@ -184,7 +237,7 @@ impl ChildBySource for EnumId {
         let arena_map = arena_map.as_ref();
         for (local_id, source) in arena_map.value.iter() {
             let id = EnumVariantId { parent: *self, local_id };
-            res[keys::VARIANT].insert(source.clone(), id)
+            res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id)
         }
     }
 }
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index 471c7330d7..753084fb4b 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -2,7 +2,7 @@
 
 use std::sync::Arc;
 
-use hir_expand::{name::Name, AstId, ExpandResult, InFile, MacroCallId};
+use hir_expand::{name::Name, AstId, ExpandResult, InFile};
 use syntax::ast;
 
 use crate::{
@@ -184,8 +184,6 @@ pub struct TraitData {
     /// method calls to this trait's methods when the receiver is an array and the crate edition is
     /// 2015 or 2018.
     pub skip_array_during_method_dispatch: bool,
-    // box it as the vec is usually empty anyways
-    pub attribute_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
 }
 
 impl TraitData {
@@ -209,8 +207,15 @@ impl TraitData {
             .by_key("rustc_skip_array_during_method_dispatch")
             .exists();
 
-        let (items, attribute_calls) =
-            do_collect(db, module_id, &mut expander, &tr_def.items, tr_loc.id.tree_id(), container);
+        let items = collect_items(
+            db,
+            module_id,
+            &mut expander,
+            tr_def.items.iter().copied(),
+            tr_loc.id.tree_id(),
+            container,
+            100,
+        );
 
         Arc::new(TraitData {
             name,
@@ -219,7 +224,6 @@ impl TraitData {
             is_unsafe,
             visibility,
             skip_array_during_method_dispatch,
-            attribute_calls,
         })
     }
 
@@ -243,10 +247,6 @@ impl TraitData {
             _ => None,
         })
     }
-
-    pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
-        self.attribute_calls.iter().flat_map(|it| it.iter()).copied()
-    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -255,8 +255,6 @@ pub struct ImplData {
     pub self_ty: Interned<TypeRef>,
     pub items: Vec<AssocItemId>,
     pub is_negative: bool,
-    // box it as the vec is usually empty anyways
-    pub attribute_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
 }
 
 impl ImplData {
@@ -273,21 +271,18 @@ impl ImplData {
         let container = ItemContainerId::ImplId(id);
         let mut expander = Expander::new(db, impl_loc.id.file_id(), module_id);
 
-        let (items, attribute_calls) = do_collect(
+        let items = collect_items(
             db,
             module_id,
             &mut expander,
-            &impl_def.items,
+            impl_def.items.iter().copied(),
             impl_loc.id.tree_id(),
             container,
+            100,
         );
         let items = items.into_iter().map(|(_, item)| item).collect();
 
-        Arc::new(ImplData { target_trait, self_ty, items, is_negative, attribute_calls })
-    }
-
-    pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
-        self.attribute_calls.iter().flat_map(|it| it.iter()).copied()
+        Arc::new(ImplData { target_trait, self_ty, items, is_negative })
     }
 }
 
@@ -338,47 +333,17 @@ impl StaticData {
     }
 }
 
-fn do_collect(
-    db: &dyn DefDatabase,
-    module_id: ModuleId,
-    expander: &mut Expander,
-    assoc_items: &[AssocItem],
-    tree_id: item_tree::TreeId,
-    container: ItemContainerId,
-) -> (Vec<(Name, AssocItemId)>, Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>) {
-    let mut items = Vec::new();
-    let mut attribute_calls = Vec::new();
-
-    collect_items(
-        db,
-        &mut items,
-        &mut attribute_calls,
-        module_id,
-        expander,
-        assoc_items.iter().copied(),
-        tree_id,
-        container,
-        100,
-    );
-
-    let attribute_calls =
-        if attribute_calls.is_empty() { None } else { Some(Box::new(attribute_calls)) };
-    (items, attribute_calls)
-}
-
 fn collect_items(
     db: &dyn DefDatabase,
-    items: &mut Vec<(Name, AssocItemId)>,
-    attr_calls: &mut Vec<(AstId<ast::Item>, MacroCallId)>,
     module: ModuleId,
     expander: &mut Expander,
     assoc_items: impl Iterator<Item = AssocItem>,
     tree_id: item_tree::TreeId,
     container: ItemContainerId,
     limit: usize,
-) {
+) -> Vec<(Name, AssocItemId)> {
     if limit == 0 {
-        return;
+        return Vec::new();
     }
 
     let item_tree = tree_id.item_tree(db);
@@ -386,6 +351,7 @@ fn collect_items(
     let cfg_options = &crate_graph[module.krate].cfg_options;
     let def_map = module.def_map(db);
 
+    let mut items = Vec::new();
     'items: for item in assoc_items {
         let attrs = item_tree.attrs(db, module.krate, ModItem::from(item).into());
         if !attrs.is_cfg_enabled(cfg_options) {
@@ -393,15 +359,15 @@ fn collect_items(
         }
 
         for attr in &*attrs {
-            let ast_id = AstId::new(expander.current_file_id(), item.ast_id(&item_tree).upcast());
-            let ast_id_with_path = AstIdWithPath { path: (*attr.path).clone(), ast_id };
-
+            let ast_id = AstIdWithPath {
+                path: (*attr.path).clone(),
+                ast_id: AstId::new(expander.current_file_id(), item.ast_id(&item_tree).upcast()),
+            };
             if let Ok(ResolvedAttr::Macro(call_id)) =
-                def_map.resolve_attr_macro(db, module.local_id, ast_id_with_path, attr)
+                def_map.resolve_attr_macro(db, module.local_id, ast_id, attr)
             {
-                attr_calls.push((ast_id, call_id));
                 let res = expander.enter_expand_id(db, call_id);
-                collect_macro_items(db, items, attr_calls, module, expander, container, limit, res);
+                items.extend(collect_macro_items(db, module, expander, container, limit, res));
                 continue 'items;
             }
         }
@@ -435,32 +401,34 @@ fn collect_items(
                 let res = expander.enter_expand(db, call);
 
                 if let Ok(res) = res {
-                    collect_macro_items(
-                        db, items, attr_calls, module, expander, container, limit, res,
-                    );
+                    items.extend(collect_macro_items(db, module, expander, container, limit, res));
                 }
             }
         }
     }
+
+    items
 }
 
 fn collect_macro_items(
     db: &dyn DefDatabase,
-    items: &mut Vec<(Name, AssocItemId)>,
-    attr_calls: &mut Vec<(AstId<ast::Item>, MacroCallId)>,
     module: ModuleId,
     expander: &mut Expander,
     container: ItemContainerId,
     limit: usize,
     res: ExpandResult<Option<(Mark, ast::MacroItems)>>,
-) {
+) -> Vec<(Name, AssocItemId)> {
     if let Some((mark, mac)) = res.value {
         let src: InFile<ast::MacroItems> = expander.to_source(mac);
         let tree_id = item_tree::TreeId::new(src.file_id, None);
         let item_tree = tree_id.item_tree(db);
         let iter = item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
-        collect_items(db, items, attr_calls, module, expander, iter, tree_id, container, limit - 1);
+        let items = collect_items(db, module, expander, iter, tree_id, container, limit - 1);
 
         expander.exit(db, mark);
+
+        return items;
     }
+
+    Vec::new()
 }
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 7febf1b5e8..c4bd5b39c5 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -454,17 +454,14 @@ impl HasChildSource<LocalConstParamId> for GenericDefId {
 }
 
 impl ChildBySource for GenericDefId {
-    fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
-        let (gfile_id, generic_params_list) = file_id_and_params_of(*self, db);
-        if gfile_id != file_id {
-            return;
-        }
-
+    fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) {
         let generic_params = db.generic_params(*self);
         let mut types_idx_iter = generic_params.types.iter().map(|(idx, _)| idx);
         let lts_idx_iter = generic_params.lifetimes.iter().map(|(idx, _)| idx);
         let consts_idx_iter = generic_params.consts.iter().map(|(idx, _)| idx);
 
+        let (file_id, generic_params_list) = file_id_and_params_of(*self, db);
+
         // For traits the first type index is `Self`, skip it.
         if let GenericDefId::TraitId(_) = *self {
             types_idx_iter.next().unwrap(); // advance_by(1);
@@ -473,15 +470,15 @@ impl ChildBySource for GenericDefId {
         if let Some(generic_params_list) = generic_params_list {
             for (local_id, ast_param) in types_idx_iter.zip(generic_params_list.type_params()) {
                 let id = TypeParamId { parent: *self, local_id };
-                res[keys::TYPE_PARAM].insert(ast_param, id);
+                res[keys::TYPE_PARAM].insert(InFile::new(file_id, ast_param), id);
             }
             for (local_id, ast_param) in lts_idx_iter.zip(generic_params_list.lifetime_params()) {
                 let id = LifetimeParamId { parent: *self, local_id };
-                res[keys::LIFETIME_PARAM].insert(ast_param, id);
+                res[keys::LIFETIME_PARAM].insert(InFile::new(file_id, ast_param), id);
             }
             for (local_id, ast_param) in consts_idx_iter.zip(generic_params_list.const_params()) {
                 let id = ConstParamId { parent: *self, local_id };
-                res[keys::CONST_PARAM].insert(ast_param, id);
+                res[keys::CONST_PARAM].insert(InFile::new(file_id, ast_param), id);
             }
         }
     }
diff --git a/crates/hir_def/src/keys.rs b/crates/hir_def/src/keys.rs
index 93c92c1b9c..3a9cf6eb81 100644
--- a/crates/hir_def/src/keys.rs
+++ b/crates/hir_def/src/keys.rs
@@ -2,7 +2,7 @@
 
 use std::marker::PhantomData;
 
-use hir_expand::{MacroCallId, MacroDefId};
+use hir_expand::{InFile, MacroCallId, MacroDefId};
 use rustc_hash::FxHashMap;
 use syntax::{ast, AstNode, AstPtr};
 
@@ -13,7 +13,7 @@ use crate::{
     StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId,
 };
 
-pub type Key<K, V> = crate::dyn_map::Key<K, V, AstPtrPolicy<K, V>>;
+pub type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>;
 
 pub const FUNCTION: Key<ast::Fn, FunctionId> = Key::new();
 pub const CONST: Key<ast::Const, ConstId> = Key::new();
@@ -32,7 +32,7 @@ pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new();
 pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new();
 pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
 
-pub const MACRO: Key<ast::Macro, MacroDefId> = Key::new();
+pub const MACRO_CALL: Key<ast::Macro, MacroDefId> = Key::new();
 pub const ATTR_MACRO_CALL: Key<ast::Item, MacroCallId> = Key::new();
 pub const DERIVE_MACRO_CALL: Key<ast::Attr, (AttrId, Box<[Option<MacroCallId>]>)> = Key::new();
 
@@ -47,17 +47,17 @@ pub struct AstPtrPolicy<AST, ID> {
 }
 
 impl<AST: AstNode + 'static, ID: 'static> Policy for AstPtrPolicy<AST, ID> {
-    type K = AST;
+    type K = InFile<AST>;
     type V = ID;
-    fn insert(map: &mut DynMap, key: AST, value: ID) {
-        let key = AstPtr::new(&key);
+    fn insert(map: &mut DynMap, key: InFile<AST>, value: ID) {
+        let key = key.as_ref().map(AstPtr::new);
         map.map
-            .entry::<FxHashMap<AstPtr<AST>, ID>>()
+            .entry::<FxHashMap<InFile<AstPtr<AST>>, ID>>()
             .or_insert_with(Default::default)
             .insert(key, value);
     }
-    fn get<'a>(map: &'a DynMap, key: &AST) -> Option<&'a ID> {
-        let key = AstPtr::new(key);
-        map.map.get::<FxHashMap<AstPtr<AST>, ID>>()?.get(&key)
+    fn get<'a>(map: &'a DynMap, key: &InFile<AST>) -> Option<&'a ID> {
+        let key = key.as_ref().map(AstPtr::new);
+        map.map.get::<FxHashMap<InFile<AstPtr<AST>>, ID>>()?.get(&key)
     }
 }
diff --git a/crates/hir_def/src/lang_item.rs b/crates/hir_def/src/lang_item.rs
index 6797a77de5..8778501845 100644
--- a/crates/hir_def/src/lang_item.rs
+++ b/crates/hir_def/src/lang_item.rs
@@ -144,8 +144,8 @@ impl LangItems {
         let _p = profile::span("lang_item_query");
         let lang_items = db.crate_lang_items(start_crate);
         let start_crate_target = lang_items.items.get(&item);
-        if let Some(target) = start_crate_target {
-            return Some(*target);
+        if let Some(&target) = start_crate_target {
+            return Some(target);
         }
         db.crate_graph()[start_crate]
             .dependencies
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 5e49d3f6af..5f190b8727 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -350,53 +350,6 @@ impl MacroCallKind {
         }
     }
 
-    /// Returns the original file range that best describes the location of this macro call.
-    ///
-    /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros
-    /// get the whole `ast::MacroCall`, attribute macros get the attribute's range, and derives
-    /// get only the specific derive that is being referred to.
-    pub fn original_call_range(self, db: &dyn db::AstDatabase) -> FileRange {
-        let mut kind = self;
-        loop {
-            match kind.file_id().0 {
-                HirFileIdRepr::MacroFile(file) => {
-                    kind = db.lookup_intern_macro_call(file.macro_call_id).kind;
-                }
-                _ => break,
-            }
-        }
-
-        // `call_id` is now the outermost macro call, so its location is in a real file.
-        let file_id = match kind.file_id().0 {
-            HirFileIdRepr::FileId(it) => it,
-            HirFileIdRepr::MacroFile(_) => unreachable!("encountered unexpected macro file"),
-        };
-        let range = match kind {
-            MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
-            MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
-                // FIXME: should be the range of the macro name, not the whole derive
-                ast_id
-                    .to_node(db)
-                    .doc_comments_and_attrs()
-                    .nth(derive_attr_index as usize)
-                    .expect("missing derive")
-                    .expect_right("derive is a doc comment?")
-                    .syntax()
-                    .text_range()
-            }
-            MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => ast_id
-                .to_node(db)
-                .doc_comments_and_attrs()
-                .nth(invoc_attr_index as usize)
-                .expect("missing attribute")
-                .expect_right("attribute macro is a doc comment?")
-                .syntax()
-                .text_range(),
-        };
-
-        FileRange { range, file_id }
-    }
-
     fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> {
         match self {
             MacroCallKind::FnLike { ast_id, .. } => {
@@ -670,13 +623,15 @@ impl<'a> InFile<&'a SyntaxNode> {
         }
 
         // Fall back to whole macro call.
-        match self.file_id.0 {
-            HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
-            HirFileIdRepr::MacroFile(mac_file) => {
-                let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
-                loc.kind.original_call_range(db)
-            }
+        let mut node = self.cloned();
+        while let Some(call_node) = node.file_id.call_node(db) {
+            node = call_node;
         }
+
+        let orig_file = node.file_id.original_file(db);
+        assert_eq!(node.file_id, orig_file.into());
+
+        FileRange { file_id: orig_file, range: node.value.text_range() }
     }
 
     /// Attempts to map the syntax node back up its macro calls.
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 3636ab6213..4dcda0fcdc 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -309,26 +309,6 @@ pub mod known {
         wrapping_mul,
         wrapping_sub,
         // known methods of lang items
-        add,
-        mul,
-        sub,
-        div,
-        rem,
-        shl,
-        shr,
-        bitxor,
-        bitor,
-        bitand,
-        add_assign,
-        mul_assign,
-        sub_assign,
-        div_assign,
-        rem_assign,
-        shl_assign,
-        shr_assign,
-        bitxor_assign,
-        bitor_assign,
-        bitand_assign,
         eq,
         ne,
         ge,
@@ -336,12 +316,38 @@ pub mod known {
         le,
         lt,
         // lang items
-        not,
-        neg,
+        add_assign,
+        add,
+        bitand_assign,
+        bitand,
+        bitor_assign,
+        bitor,
+        bitxor_assign,
+        bitxor,
+        deref_mut,
+        deref,
+        div_assign,
+        div,
+        fn_mut,
+        fn_once,
         future_trait,
-        owned_box,
         index,
-        partial_ord
+        index_mut,
+        mul_assign,
+        mul,
+        neg,
+        not,
+        owned_box,
+        partial_ord,
+        r#fn,
+        rem_assign,
+        rem,
+        shl_assign,
+        shl,
+        shr_assign,
+        shr,
+        sub_assign,
+        sub,
     );
 
     // self/Self cannot be used as an identifier
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index f02f4ac024..95d1550afc 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -151,12 +151,6 @@ impl<'a> HirFormatter<'a> {
                 write!(self, "{}", sep)?;
             }
             first = false;
-
-            // Abbreviate multiple omitted types with a single ellipsis.
-            if self.should_truncate() {
-                return write!(self, "{}", TYPE_HINT_TRUNCATION);
-            }
-
             e.hir_fmt(self)?;
         }
         Ok(())
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 5e6f0ef6a5..949c6dc686 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -1514,25 +1514,4 @@ fn func$0() {
             "#]],
         )
     }
-
-    #[test]
-    fn attr_assoc_item() {
-        check(
-            r#"
-//- proc_macros: identity
-
-trait Trait {
-    #[proc_macros::identity]
-    fn func() {
-        Self::func$0();
-    }
-}
-"#,
-            expect![[r#"
-                func Function FileId(0) 48..87 51..55
-
-                FileId(0) 74..78
-            "#]],
-        )
-    }
 }
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 3105b28911..a10eca10d1 100644
--- a/crates/ide_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide_assists/src/handlers/add_missing_impl_members.rs
@@ -942,366 +942,4 @@ impl FooB for Foo {
 "#,
         )
     }
-
-    #[test]
-    fn test_assoc_type_when_trait_with_same_name_in_scope() {
-        check_assist(
-            add_missing_impl_members,
-            r#"
-pub trait Foo {}
-
-pub trait Types {
-    type Foo;
-}
-
-pub trait Behavior<T: Types> {
-    fn reproduce(&self, foo: T::Foo);
-}
-
-pub struct Impl;
-
-impl<T: Types> Behavior<T> for Impl { $0 }"#,
-            r#"
-pub trait Foo {}
-
-pub trait Types {
-    type Foo;
-}
-
-pub trait Behavior<T: Types> {
-    fn reproduce(&self, foo: T::Foo);
-}
-
-pub struct Impl;
-
-impl<T: Types> Behavior<T> for Impl {
-    fn reproduce(&self, foo: <T as Types>::Foo) {
-        ${0:todo!()}
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn test_assoc_type_on_concrete_type() {
-        check_assist(
-            add_missing_impl_members,
-            r#"
-pub trait Types {
-    type Foo;
-}
-
-impl Types for u32 {
-    type Foo = bool;
-}
-
-pub trait Behavior<T: Types> {
-    fn reproduce(&self, foo: T::Foo);
-}
-
-pub struct Impl;
-
-impl Behavior<u32> for Impl { $0 }"#,
-            r#"
-pub trait Types {
-    type Foo;
-}
-
-impl Types for u32 {
-    type Foo = bool;
-}
-
-pub trait Behavior<T: Types> {
-    fn reproduce(&self, foo: T::Foo);
-}
-
-pub struct Impl;
-
-impl Behavior<u32> for Impl {
-    fn reproduce(&self, foo: <u32 as Types>::Foo) {
-        ${0:todo!()}
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn test_assoc_type_on_concrete_type_qualified() {
-        check_assist(
-            add_missing_impl_members,
-            r#"
-pub trait Types {
-    type Foo;
-}
-
-impl Types for std::string::String {
-    type Foo = bool;
-}
-
-pub trait Behavior<T: Types> {
-    fn reproduce(&self, foo: T::Foo);
-}
-
-pub struct Impl;
-
-impl Behavior<std::string::String> for Impl { $0 }"#,
-            r#"
-pub trait Types {
-    type Foo;
-}
-
-impl Types for std::string::String {
-    type Foo = bool;
-}
-
-pub trait Behavior<T: Types> {
-    fn reproduce(&self, foo: T::Foo);
-}
-
-pub struct Impl;
-
-impl Behavior<std::string::String> for Impl {
-    fn reproduce(&self, foo: <std::string::String as Types>::Foo) {
-        ${0:todo!()}
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn test_assoc_type_on_concrete_type_multi_option_ambiguous() {
-        check_assist(
-            add_missing_impl_members,
-            r#"
-pub trait Types {
-    type Foo;
-}
-
-pub trait Types2 {
-    type Foo;
-}
-
-impl Types for u32 {
-    type Foo = bool;
-}
-
-impl Types2 for u32 {
-    type Foo = String;
-}
-
-pub trait Behavior<T: Types + Types2> {
-    fn reproduce(&self, foo: <T as Types2>::Foo);
-}
-
-pub struct Impl;
-
-impl Behavior<u32> for Impl { $0 }"#,
-            r#"
-pub trait Types {
-    type Foo;
-}
-
-pub trait Types2 {
-    type Foo;
-}
-
-impl Types for u32 {
-    type Foo = bool;
-}
-
-impl Types2 for u32 {
-    type Foo = String;
-}
-
-pub trait Behavior<T: Types + Types2> {
-    fn reproduce(&self, foo: <T as Types2>::Foo);
-}
-
-pub struct Impl;
-
-impl Behavior<u32> for Impl {
-    fn reproduce(&self, foo: <u32 as Types2>::Foo) {
-        ${0:todo!()}
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn test_assoc_type_on_concrete_type_multi_option() {
-        check_assist(
-            add_missing_impl_members,
-            r#"
-pub trait Types {
-    type Foo;
-}
-
-pub trait Types2 {
-    type Bar;
-}
-
-impl Types for u32 {
-    type Foo = bool;
-}
-
-impl Types2 for u32 {
-    type Bar = String;
-}
-
-pub trait Behavior<T: Types + Types2> {
-    fn reproduce(&self, foo: T::Bar);
-}
-
-pub struct Impl;
-
-impl Behavior<u32> for Impl { $0 }"#,
-            r#"
-pub trait Types {
-    type Foo;
-}
-
-pub trait Types2 {
-    type Bar;
-}
-
-impl Types for u32 {
-    type Foo = bool;
-}
-
-impl Types2 for u32 {
-    type Bar = String;
-}
-
-pub trait Behavior<T: Types + Types2> {
-    fn reproduce(&self, foo: T::Bar);
-}
-
-pub struct Impl;
-
-impl Behavior<u32> for Impl {
-    fn reproduce(&self, foo: <u32 as Types2>::Bar) {
-        ${0:todo!()}
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn test_assoc_type_on_concrete_type_multi_option_foreign() {
-        check_assist(
-            add_missing_impl_members,
-            r#"
-mod bar {
-    pub trait Types2 {
-        type Bar;
-    }
-}
-
-pub trait Types {
-    type Foo;
-}
-
-impl Types for u32 {
-    type Foo = bool;
-}
-
-impl bar::Types2 for u32 {
-    type Bar = String;
-}
-
-pub trait Behavior<T: Types + bar::Types2> {
-    fn reproduce(&self, foo: T::Bar);
-}
-
-pub struct Impl;
-
-impl Behavior<u32> for Impl { $0 }"#,
-            r#"
-mod bar {
-    pub trait Types2 {
-        type Bar;
-    }
-}
-
-pub trait Types {
-    type Foo;
-}
-
-impl Types for u32 {
-    type Foo = bool;
-}
-
-impl bar::Types2 for u32 {
-    type Bar = String;
-}
-
-pub trait Behavior<T: Types + bar::Types2> {
-    fn reproduce(&self, foo: T::Bar);
-}
-
-pub struct Impl;
-
-impl Behavior<u32> for Impl {
-    fn reproduce(&self, foo: <u32 as bar::Types2>::Bar) {
-        ${0:todo!()}
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn test_transform_path_in_path_expr() {
-        check_assist(
-            add_missing_default_members,
-            r#"
-pub trait Const {
-    const FOO: u32;
-}
-
-pub trait Trait<T: Const> {
-    fn foo() -> bool {
-        match T::FOO {
-            0 => true,
-            _ => false,
-        }
-    }
-}
-
-impl Const for u32 {
-    const FOO: u32 = 1;
-}
-
-struct Impl;
-
-impl Trait<u32> for Impl { $0 }"#,
-            r#"
-pub trait Const {
-    const FOO: u32;
-}
-
-pub trait Trait<T: Const> {
-    fn foo() -> bool {
-        match T::FOO {
-            0 => true,
-            _ => false,
-        }
-    }
-}
-
-impl Const for u32 {
-    const FOO: u32 = 1;
-}
-
-struct Impl;
-
-impl Trait<u32> for Impl {
-    $0fn foo() -> bool {
-        match <u32 as Const>::FOO {
-            0 => true,
-            _ => false,
-        }
-    }
-}"#,
-        );
-    }
 }
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 26f6c17805..0baca08ca9 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -3,7 +3,7 @@
 use std::iter;
 
 use base_db::SourceDatabaseExt;
-use hir::{Local, Name, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo};
+use hir::{HasAttrs, Local, Name, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo};
 use ide_db::{
     active_parameter::ActiveParameter,
     base_db::{FilePosition, SourceDatabase},
@@ -85,6 +85,7 @@ pub(crate) enum ParamKind {
     Function,
     Closure,
 }
+
 /// `CompletionContext` is created early during completion to figure out, where
 /// exactly is the cursor, syntax-wise.
 #[derive(Debug)]
@@ -120,6 +121,7 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) lifetime_ctx: Option<LifetimeContext>,
     pub(super) pattern_ctx: Option<PatternContext>,
     pub(super) path_context: Option<PathCompletionContext>,
+
     pub(super) locals: Vec<(Name, Local)>,
 
     no_completion_required: bool,
@@ -308,6 +310,14 @@ impl<'a> CompletionContext<'a> {
         self.token.kind() == BANG && self.token.parent().map_or(false, |it| it.kind() == MACRO_CALL)
     }
 
+    /// Whether the given trait is an operator trait or not.
+    pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
+        match trait_.attrs(self.db).lang() {
+            Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()),
+            None => false,
+        }
+    }
+
     /// 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");
@@ -388,6 +398,7 @@ impl<'a> CompletionContext<'a> {
                 locals.push((name, local));
             }
         });
+
         let mut ctx = CompletionContext {
             sema,
             scope,
@@ -889,6 +900,7 @@ fn pattern_context_for(pat: ast::Pat) -> PatternContext {
         });
     PatternContext { refutability, is_param, has_type_ascription }
 }
+
 fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
     syntax.covering_element(range).ancestors().find_map(N::cast)
 }
@@ -915,6 +927,37 @@ fn has_ref(token: &SyntaxToken) -> bool {
     token.kind() == T![&]
 }
 
+const OP_TRAIT_LANG_NAMES: &[&str] = &[
+    "add_assign",
+    "add",
+    "bitand_assign",
+    "bitand",
+    "bitor_assign",
+    "bitor",
+    "bitxor_assign",
+    "bitxor",
+    "deref_mut",
+    "deref",
+    "div_assign",
+    "div",
+    "fn_mut",
+    "fn_once",
+    "fn",
+    "index_mut",
+    "index",
+    "mul_assign",
+    "mul",
+    "neg",
+    "not",
+    "rem_assign",
+    "rem",
+    "shl_assign",
+    "shl",
+    "shr_assign",
+    "shr",
+    "sub",
+    "sub",
+];
 #[cfg(test)]
 mod tests {
     use expect_test::{expect, Expect};
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index 4a6e034dc9..8ac4291078 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -139,6 +139,8 @@ pub struct CompletionRelevance {
     /// }
     /// ```
     pub is_local: bool,
+    /// Set for method completions of the `core::ops` family.
+    pub is_op_method: bool,
     /// This is set in cases like these:
     ///
     /// ```
@@ -175,6 +177,7 @@ pub enum CompletionRelevanceTypeMatch {
 }
 
 impl CompletionRelevance {
+    const BASE_LINE: u32 = 1;
     /// Provides a relevance score. Higher values are more relevant.
     ///
     /// The absolute value of the relevance score is not meaningful, for
@@ -185,7 +188,7 @@ impl CompletionRelevance {
     /// See is_relevant if you need to make some judgement about score
     /// in an absolute sense.
     pub fn score(&self) -> u32 {
-        let mut score = 0;
+        let mut score = Self::BASE_LINE;
 
         if self.exact_name_match {
             score += 1;
@@ -198,6 +201,9 @@ impl CompletionRelevance {
         if self.is_local {
             score += 1;
         }
+        if self.is_op_method {
+            score -= 1;
+        }
         if self.exact_postfix_snippet_match {
             score += 100;
         }
@@ -208,7 +214,7 @@ impl CompletionRelevance {
     /// some threshold such that we think it is especially likely
     /// to be relevant.
     pub fn is_relevant(&self) -> bool {
-        self.score() > 0
+        self.score() > (Self::BASE_LINE + 1)
     }
 }
 
@@ -558,6 +564,7 @@ mod tests {
         // This test asserts that the relevance score for these items is ascending, and
         // that any items in the same vec have the same score.
         let expected_relevance_order = vec![
+            vec![CompletionRelevance { is_op_method: true, ..CompletionRelevance::default() }],
             vec![CompletionRelevance::default()],
             vec![
                 CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() },
@@ -588,10 +595,8 @@ mod tests {
                 ..CompletionRelevance::default()
             }],
             vec![CompletionRelevance {
-                exact_name_match: false,
-                type_match: None,
-                is_local: false,
                 exact_postfix_snippet_match: true,
+                ..CompletionRelevance::default()
             }],
         ];
 
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index cd71ad1eea..15dacc8e46 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -400,6 +400,7 @@ mod tests {
                 (relevance.exact_name_match, "name"),
                 (relevance.is_local, "local"),
                 (relevance.exact_postfix_snippet_match, "snippet"),
+                (relevance.is_op_method, "op_method"),
             ]
             .into_iter()
             .filter_map(|(cond, desc)| if cond { Some(desc) } else { None })
@@ -580,6 +581,7 @@ fn main() { let _: m::Spam = S$0 }
                                 Exact,
                             ),
                             is_local: false,
+                            is_op_method: false,
                             exact_postfix_snippet_match: false,
                         },
                         trigger_call_info: true,
@@ -600,6 +602,7 @@ fn main() { let _: m::Spam = S$0 }
                                 Exact,
                             ),
                             is_local: false,
+                            is_op_method: false,
                             exact_postfix_snippet_match: false,
                         },
                     },
@@ -685,6 +688,7 @@ fn foo() { A { the$0 } }
                                 CouldUnify,
                             ),
                             is_local: false,
+                            is_op_method: false,
                             exact_postfix_snippet_match: false,
                         },
                     },
@@ -1349,6 +1353,23 @@ fn main() {
         )
     }
 
+    #[test]
+    fn op_method_relevances() {
+        check_relevance(
+            r#"
+#[lang = "sub"]
+trait Sub {
+    fn sub(self, other: Self) -> Self { self }
+}
+impl Sub for u32 {}
+fn foo(a: u32) { a.$0 }
+"#,
+            expect![[r#"
+                me sub(…) (as Sub) [op_method]
+            "#]],
+        )
+    }
+
     #[test]
     fn struct_field_method_ref() {
         check_kinds(
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index bd46e1fefb..fc2cb933db 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -70,6 +70,13 @@ fn render(
     item.set_relevance(CompletionRelevance {
         type_match: compute_type_match(completion, &ret_type),
         exact_name_match: compute_exact_name_match(completion, &call),
+        is_op_method: match func_type {
+            FuncType::Method(_) => func
+                .as_assoc_item(ctx.db())
+                .and_then(|trait_| trait_.containing_trait_or_trait_impl(ctx.db()))
+                .map_or(false, |trait_| completion.is_ops_trait(trait_)),
+            _ => false,
+        },
         ..CompletionRelevance::default()
     });
 
diff --git a/crates/ide_db/src/path_transform.rs b/crates/ide_db/src/path_transform.rs
index 524af7fe8f..1b4f793a50 100644
--- a/crates/ide_db/src/path_transform.rs
+++ b/crates/ide_db/src/path_transform.rs
@@ -118,20 +118,14 @@ struct Ctx<'a> {
 
 impl<'a> Ctx<'a> {
     fn apply(&self, item: &SyntaxNode) {
-        // `transform_path` may update a node's parent and that would break the
-        // tree traversal. Thus all paths in the tree are collected into a vec
-        // so that such operation is safe.
-        let paths = item
-            .preorder()
-            .filter_map(|event| match event {
-                syntax::WalkEvent::Enter(_) => None,
-                syntax::WalkEvent::Leave(node) => Some(node),
-            })
-            .filter_map(ast::Path::cast)
-            .collect::<Vec<_>>();
-
-        for path in paths {
-            self.transform_path(path);
+        for event in item.preorder() {
+            let node = match event {
+                syntax::WalkEvent::Enter(_) => continue,
+                syntax::WalkEvent::Leave(it) => it,
+            };
+            if let Some(path) = ast::Path::cast(node.clone()) {
+                self.transform_path(path);
+            }
         }
     }
     fn transform_path(&self, path: ast::Path) -> Option<()> {
@@ -151,60 +145,10 @@ impl<'a> Ctx<'a> {
         match resolution {
             hir::PathResolution::TypeParam(tp) => {
                 if let Some(subst) = self.substs.get(&tp) {
-                    let parent = path.syntax().parent()?;
-                    if let Some(parent) = ast::Path::cast(parent.clone()) {
-                        // Path inside path means that there is an associated
-                        // type/constant on the type parameter. It is necessary
-                        // to fully qualify the type with `as Trait`. Even
-                        // though it might be unnecessary if `subst` is generic
-                        // type, always fully qualifying the path is safer
-                        // because of potential clash of associated types from
-                        // multiple traits
-
-                        let trait_ref = find_trait_for_assoc_item(
-                            self.source_scope,
-                            tp,
-                            parent.segment()?.name_ref()?,
-                        )
-                        .and_then(|trait_ref| {
-                            let found_path = self.target_module.find_use_path(
-                                self.source_scope.db.upcast(),
-                                hir::ModuleDef::Trait(trait_ref),
-                            )?;
-                            match ast::make::ty_path(mod_path_to_ast(&found_path)) {
-                                ast::Type::PathType(path_ty) => Some(path_ty),
-                                _ => None,
-                            }
-                        });
-
-                        let segment = ast::make::path_segment_ty(subst.clone(), trait_ref);
-                        let qualified =
-                            ast::make::path_from_segments(std::iter::once(segment), false);
-                        ted::replace(path.syntax(), qualified.clone_for_update().syntax());
-                    } else if let Some(path_ty) = ast::PathType::cast(parent) {
-                        ted::replace(
-                            path_ty.syntax(),
-                            subst.clone_subtree().clone_for_update().syntax(),
-                        );
-                    } else {
-                        ted::replace(
-                            path.syntax(),
-                            subst.clone_subtree().clone_for_update().syntax(),
-                        );
-                    }
+                    ted::replace(path.syntax(), subst.clone_subtree().clone_for_update().syntax())
                 }
             }
             hir::PathResolution::Def(def) => {
-                if let hir::ModuleDef::Trait(_) = def {
-                    if matches!(path.segment()?.kind()?, ast::PathSegmentKind::Type { .. }) {
-                        // `speculative_resolve` resolves segments like `<T as
-                        // Trait>` into `Trait`, but just the trait name should
-                        // not be used as the replacement of the original
-                        // segment.
-                        return None;
-                    }
-                }
-
                 let found_path =
                     self.target_module.find_use_path(self.source_scope.db.upcast(), def)?;
                 let res = mod_path_to_ast(&found_path).clone_for_update();
@@ -251,34 +195,3 @@ fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<
 
     Some(result)
 }
-
-fn find_trait_for_assoc_item(
-    scope: &SemanticsScope,
-    type_param: hir::TypeParam,
-    assoc_item: ast::NameRef,
-) -> Option<hir::Trait> {
-    let db = scope.db;
-    let trait_bounds = type_param.trait_bounds(db);
-
-    let assoc_item_name = assoc_item.text();
-
-    for trait_ in trait_bounds {
-        let names = trait_.items(db).into_iter().filter_map(|item| match item {
-            hir::AssocItem::TypeAlias(ta) => Some(ta.name(db)),
-            hir::AssocItem::Const(cst) => cst.name(db),
-            _ => None,
-        });
-
-        for name in names {
-            if assoc_item_name.as_str() == name.as_text()?.as_str() {
-                // It is fine to return the first match because in case of
-                // multiple possibilities, the exact trait must be disambiguated
-                // in the definition of trait being implemented, so this search
-                // should not be needed.
-                return Some(trait_);
-            }
-        }
-    }
-
-    None
-}
diff --git a/crates/proc_macro_srv/src/abis/abi_1_56/mod.rs b/crates/proc_macro_srv/src/abis/abi_1_56/mod.rs
index ee1c2d4553..96b71d3f21 100644
--- a/crates/proc_macro_srv/src/abis/abi_1_56/mod.rs
+++ b/crates/proc_macro_srv/src/abis/abi_1_56/mod.rs
@@ -51,7 +51,7 @@ impl Abi {
                         &proc_macro::bridge::server::SameThread,
                         rustc_server::Rustc::default(),
                         parsed_body,
-                        true,
+                        false,
                     );
                     return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
                 }
@@ -62,7 +62,7 @@ impl Abi {
                         &proc_macro::bridge::server::SameThread,
                         rustc_server::Rustc::default(),
                         parsed_body,
-                        true,
+                        false,
                     );
                     return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
                 }
@@ -74,7 +74,7 @@ impl Abi {
                         rustc_server::Rustc::default(),
                         parsed_attributes,
                         parsed_body,
-                        true,
+                        false,
                     );
                     return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
                 }
diff --git a/crates/proc_macro_srv/src/abis/abi_1_58/mod.rs b/crates/proc_macro_srv/src/abis/abi_1_58/mod.rs
index dd3fd8b642..4421382006 100644
--- a/crates/proc_macro_srv/src/abis/abi_1_58/mod.rs
+++ b/crates/proc_macro_srv/src/abis/abi_1_58/mod.rs
@@ -51,7 +51,7 @@ impl Abi {
                         &proc_macro::bridge::server::SameThread,
                         rustc_server::Rustc::default(),
                         parsed_body,
-                        true,
+                        false,
                     );
                     return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
                 }
@@ -62,7 +62,7 @@ impl Abi {
                         &proc_macro::bridge::server::SameThread,
                         rustc_server::Rustc::default(),
                         parsed_body,
-                        true,
+                        false,
                     );
                     return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
                 }
@@ -74,7 +74,7 @@ impl Abi {
                         rustc_server::Rustc::default(),
                         parsed_attributes,
                         parsed_body,
-                        true,
+                        false,
                     );
                     return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
                 }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 1df19ffe78..88d86ef503 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -1364,23 +1364,7 @@ fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String {
         .map(|(field, _ty, doc, default)| {
             let name = format!("rust-analyzer.{}", field.replace("_", "."));
             let doc = doc_comment_to_string(*doc);
-            if default.contains('\n') {
-                format!(
-                    r#"[[{}]]{}::
-+
---
-Default:
-----
-{}
-----
-{}
---
-"#,
-                    name, name, default, doc
-                )
-            } else {
-                format!("[[{}]]{} (default: `{}`)::\n+\n--\n{}--\n", name, name, default, doc)
-            }
+            format!("[[{}]]{} (default: `{}`)::\n+\n--\n{}--\n", name, name, default, doc)
         })
         .collect::<String>()
 }
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 09c5af210f..8e55afb949 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -162,7 +162,6 @@ pub struct MacroCall {
     pub(crate) syntax: SyntaxNode,
 }
 impl ast::HasAttrs for MacroCall {}
-impl ast::HasDocComments for MacroCall {}
 impl MacroCall {
     pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
     pub fn excl_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![!]) }
@@ -260,7 +259,6 @@ pub struct ExternBlock {
     pub(crate) syntax: SyntaxNode,
 }
 impl ast::HasAttrs for ExternBlock {}
-impl ast::HasDocComments for ExternBlock {}
 impl ExternBlock {
     pub fn abi(&self) -> Option<Abi> { support::child(&self.syntax) }
     pub fn extern_item_list(&self) -> Option<ExternItemList> { support::child(&self.syntax) }
@@ -272,7 +270,6 @@ pub struct ExternCrate {
 }
 impl ast::HasAttrs for ExternCrate {}
 impl ast::HasVisibility for ExternCrate {}
-impl ast::HasDocComments for ExternCrate {}
 impl ExternCrate {
     pub fn extern_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![extern]) }
     pub fn crate_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![crate]) }
@@ -1546,7 +1543,6 @@ pub enum Item {
     Use(Use),
 }
 impl ast::HasAttrs for Item {}
-impl ast::HasDocComments for Item {}
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum Stmt {
@@ -1601,7 +1597,6 @@ pub enum AssocItem {
     TypeAlias(TypeAlias),
 }
 impl ast::HasAttrs for AssocItem {}
-impl ast::HasDocComments for AssocItem {}
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum ExternItem {
@@ -1611,7 +1606,6 @@ pub enum ExternItem {
     TypeAlias(TypeAlias),
 }
 impl ast::HasAttrs for ExternItem {}
-impl ast::HasDocComments for ExternItem {}
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum GenericParam {
@@ -3908,9 +3902,10 @@ impl AnyHasDocComments {
 impl AstNode for AnyHasDocComments {
     fn can_cast(kind: SyntaxKind) -> bool {
         match kind {
-            MACRO_CALL | SOURCE_FILE | CONST | ENUM | EXTERN_BLOCK | EXTERN_CRATE | FN | IMPL
-            | MACRO_RULES | MACRO_DEF | MODULE | STATIC | STRUCT | TRAIT | TYPE_ALIAS | UNION
-            | USE | RECORD_FIELD | TUPLE_FIELD | VARIANT => true,
+            SOURCE_FILE | CONST | ENUM | FN | IMPL | MACRO_RULES | MACRO_DEF | MODULE | STATIC
+            | STRUCT | TRAIT | TYPE_ALIAS | UNION | USE | RECORD_FIELD | TUPLE_FIELD | VARIANT => {
+                true
+            }
             _ => false,
         }
     }
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index f80c5e382c..3575020975 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -188,14 +188,6 @@ pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
     ast_from_text(&format!("use {};", name_ref))
 }
 
-pub fn path_segment_ty(type_ref: ast::Type, trait_ref: Option<ast::PathType>) -> ast::PathSegment {
-    let text = match trait_ref {
-        Some(trait_ref) => format!("fn f(x: <{} as {}>) {{}}", type_ref, trait_ref),
-        None => format!("fn f(x: <{}>) {{}}", type_ref),
-    };
-    ast_from_text(&text)
-}
-
 pub fn path_segment_self() -> ast::PathSegment {
     ast_from_text("use self;")
 }
@@ -226,9 +218,9 @@ pub fn path_from_segments(
 ) -> ast::Path {
     let segments = segments.into_iter().map(|it| it.syntax().clone()).join("::");
     ast_from_text(&if is_abs {
-        format!("fn f(x: ::{}) {{}}", segments)
+        format!("use ::{};", segments)
     } else {
-        format!("fn f(x: {}) {{}}", segments)
+        format!("use {};", segments)
     })
 }
 
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 705aa5edac..470e9a04b3 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -772,6 +772,7 @@ impl ast::HasLoopBody for ast::ForExpr {
 }
 
 impl ast::HasAttrs for ast::AnyHasDocComments {}
+impl ast::HasDocComments for ast::Item {}
 
 impl From<ast::Adt> for ast::Item {
     fn from(it: ast::Adt) -> Self {
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index f7a533c7c2..88dcacfe83 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -146,12 +146,7 @@ Only applies when `#rust-analyzer.completion.addCallParenthesis#` is set.
 --
 Whether to add parenthesis when completing functions.
 --
-[[rust-analyzer.completion.snippets]]rust-analyzer.completion.snippets::
-+
---
-Default:
-----
-{
+[[rust-analyzer.completion.snippets]]rust-analyzer.completion.snippets (default: `{
             "Arc::new": {
                 "postfix": "arc",
                 "body": "Arc::new(${receiver})",
@@ -191,10 +186,10 @@ Default:
                 "description": "Wrap the expression in an `Option::Some`",
                 "scope": "expr"
             }
-        }
-----
+        }`)::
++
+--
 Custom completion snippets.
-
 --
 [[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`)::
 +


Test patch:
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index a04a211825..6e87147e8b 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -46,8 +46,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="comment documentation">//! </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">test</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span>
 <span class="comment documentation">//! ```</span>
 
-<span class="keyword">mod</span> <span class="module declaration">outline_module</span><span class="semicolon">;</span>
-
 <span class="comment documentation">/// ```</span>
 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"early doctests should not go boom"</span><span class="semicolon injected">;</span>
 <span class="comment documentation">/// ```</span>
@@ -172,6 +170,4 @@ It is beyond me why you'd use these when you got ///
     ```
     </span><span class="function documentation injected intra_doc_link">[`block_comments`]</span><span class="comment documentation"> tests these without indentation
 */</span>
-<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration public">block_comments2</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-
-</code></pre>
\ No newline at end of file
+<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration public">block_comments2</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 4beab9909c..c9c23318f5 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -641,14 +641,11 @@ fn main() {
 fn test_highlight_doc_comment() {
     check_highlighting(
         r#"
-//- /main.rs
 //! This is a module to test doc injection.
 //! ```
 //! fn test() {}
 //! ```
 
-mod outline_module;
-
 /// ```
 /// let _ = "early doctests should not go boom";
 /// ```
@@ -774,13 +771,6 @@ pub fn block_comments() {}
     [`block_comments`] tests these without indentation
 */
 pub fn block_comments2() {}
-
-//- /outline_module.rs
-//! This is an outline module whose purpose is to test that its inline attribute injection does not
-//! spill into its parent.
-//! ```
-//! fn test() {}
-//! ```
 "#
         .trim(),
         expect_file!["./test_data/highlight_doctest.html"],
diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs
index 4a608da9ff..ac928402f3 100644
--- a/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/crates/syntax/src/tests/sourcegen_ast.rs
@@ -793,11 +793,9 @@ fn extract_struct_traits(ast: &mut AstSrc) {
         "Const",
         "TypeAlias",
         "Impl",
-        "ExternBlock",
-        "ExternCrate",
-        "MacroCall",
         "MacroRules",
         "MacroDef",
+        "Macro",
         "Use",
     ];