about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2024-04-03 15:23:56 +0200
committerLukas Wirth <lukastw97@gmail.com>2024-06-04 15:23:00 +0200
commit98769133c6909f455618c5c06c0889d8d3df1af4 (patch)
treebbfec8e0050b88c91a4aaec550169b823895982e
parent59c3a3a86bcbc8e04cf192024aeab10c74cde07e (diff)
downloadrust-98769133c6909f455618c5c06c0889d8d3df1af4.tar.gz
rust-98769133c6909f455618c5c06c0889d8d3df1af4.zip
Try caching macro calls more aggressively
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs52
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/files.rs27
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs75
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs188
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs293
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs18
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs2
11 files changed, 385 insertions, 303 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index d2f4d7b7e56..a796ef33c60 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -395,6 +395,12 @@ impl BodySourceMap {
         self.expr_map.get(&src).copied()
     }
 
+    pub fn expansions(
+        &self,
+    ) -> impl Iterator<Item = (&InFile<AstPtr<ast::MacroCall>>, &MacroFileId)> {
+        self.expansions.iter()
+    }
+
     pub fn implicit_format_args(
         &self,
         node: InFile<&ast::FormatArgsExpr>,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
index c6d9ba6cfe4..5a5a8d302b5 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
@@ -12,6 +12,7 @@ use intern::Interned;
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
 use span::AstIdMap;
+use stdx::never;
 use syntax::{
     ast::{
         self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasLoopBody, HasName,
@@ -480,7 +481,8 @@ impl ExprCollector<'_> {
                     } else if e.const_token().is_some() {
                         Mutability::Shared
                     } else {
-                        unreachable!("parser only remaps to raw_token() if matching mutability token follows")
+                        never!("parser only remaps to raw_token() if matching mutability token follows");
+                        Mutability::Shared
                     }
                 } else {
                     Mutability::from_mutable(e.mut_token().is_some())
@@ -1006,9 +1008,9 @@ impl ExprCollector<'_> {
             Some((mark, expansion)) => {
                 // Keep collecting even with expansion errors so we can provide completions and
                 // other services in incomplete macro expressions.
-                self.source_map
-                    .expansions
-                    .insert(macro_call_ptr, self.expander.current_file_id().macro_file().unwrap());
+                if let Some(macro_file) = self.expander.current_file_id().macro_file() {
+                    self.source_map.expansions.insert(macro_call_ptr, macro_file);
+                }
                 let prev_ast_id_map = mem::replace(
                     &mut self.ast_id_map,
                     self.db.ast_id_map(self.expander.current_file_id()),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
index 0b41984bdd8..106109eb184 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
@@ -6,7 +6,7 @@
 
 use either::Either;
 use hir_expand::{attrs::collect_attrs, HirFileId};
-use syntax::ast;
+use syntax::{ast, AstPtr};
 
 use crate::{
     db::DefDatabase,
@@ -38,7 +38,7 @@ impl ChildBySource for TraitId {
 
         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);
+                res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db.upcast()), call_id);
             },
         );
         data.items.iter().for_each(|&(_, item)| {
@@ -50,9 +50,10 @@ impl ChildBySource for TraitId {
 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);
+        // FIXME: Macro calls
         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);
+                res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db.upcast()), call_id);
             },
         );
         data.items.iter().for_each(|&item| {
@@ -80,7 +81,7 @@ impl ChildBySource for ItemScope {
             .for_each(|konst| insert_item_loc(db, res, file_id, konst, keys::CONST));
         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);
+                res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db.upcast()), call_id);
             },
         );
         self.legacy_macros().for_each(|(_, ids)| {
@@ -88,7 +89,7 @@ impl ChildBySource for ItemScope {
                 if let MacroId::MacroRulesId(id) = id {
                     let loc = id.lookup(db);
                     if loc.id.file_id() == file_id {
-                        res[keys::MACRO_RULES].insert(loc.source(db).value, id);
+                        res[keys::MACRO_RULES].insert(loc.ast_ptr(db).value, id);
                     }
                 }
             })
@@ -100,12 +101,18 @@ impl ChildBySource for ItemScope {
                     if let Some((_, Either::Left(attr))) =
                         collect_attrs(&adt).nth(attr_id.ast_index())
                     {
-                        res[keys::DERIVE_MACRO_CALL].insert(attr, (attr_id, call_id, calls.into()));
+                        res[keys::DERIVE_MACRO_CALL]
+                            .insert(AstPtr::new(&attr), (attr_id, call_id, calls.into()));
                     }
                 });
             },
         );
-
+        self.iter_macro_invoc().filter(|(id, _)| id.file_id == file_id).for_each(
+            |(ast_id, &call)| {
+                let ast = ast_id.to_ptr(db.upcast());
+                res[keys::MACRO_CALL].insert(ast, call);
+            },
+        );
         fn add_module_def(
             db: &dyn DefDatabase,
             map: &mut DynMap,
@@ -155,8 +162,8 @@ 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(AstPtr::new(&source), id),
+                Either::Right(source) => res[keys::RECORD_FIELD].insert(AstPtr::new(&source), id),
             }
         }
     }
@@ -171,29 +178,30 @@ impl ChildBySource for EnumId {
 
         let tree = loc.id.item_tree(db);
         let ast_id_map = db.ast_id_map(loc.id.file_id());
-        let root = db.parse_or_expand(loc.id.file_id());
 
         db.enum_data(*self).variants.iter().for_each(|&(variant, _)| {
-            res[keys::ENUM_VARIANT].insert(
-                ast_id_map.get(tree[variant.lookup(db).id.value].ast_id).to_node(&root),
-                variant,
-            );
+            res[keys::ENUM_VARIANT]
+                .insert(ast_id_map.get(tree[variant.lookup(db).id.value].ast_id), variant);
         });
     }
 }
 
 impl ChildBySource for DefWithBodyId {
     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
-        let body = db.body(*self);
+        let (body, sm) = db.body_with_source_map(*self);
         if let &DefWithBodyId::VariantId(v) = self {
             VariantId::EnumVariantId(v).child_by_source_to(db, res, file_id)
         }
 
+        sm.expansions().filter(|(ast, _)| ast.file_id == file_id).for_each(|(ast, &exp_id)| {
+            res[keys::MACRO_CALL].insert(ast.value, exp_id.macro_call_id);
+        });
+
         for (block, def_map) in body.blocks(db) {
             // All block expressions are merged into the same map, because they logically all add
             // inner items to the containing `DefWithBodyId`.
             def_map[DefMap::ROOT].scope.child_by_source_to(db, res, file_id);
-            res[keys::BLOCK].insert(block.lookup(db).ast_id.to_node(db.upcast()), block);
+            res[keys::BLOCK].insert(block.lookup(db).ast_id.to_ptr(db.upcast()), block);
         }
     }
 }
@@ -220,13 +228,17 @@ impl ChildBySource for GenericDefId {
             {
                 let id = TypeOrConstParamId { parent: *self, local_id };
                 match ast_param {
-                    ast::TypeOrConstParam::Type(a) => res[keys::TYPE_PARAM].insert(a, id),
-                    ast::TypeOrConstParam::Const(a) => res[keys::CONST_PARAM].insert(a, id),
+                    ast::TypeOrConstParam::Type(a) => {
+                        res[keys::TYPE_PARAM].insert(AstPtr::new(&a), id)
+                    }
+                    ast::TypeOrConstParam::Const(a) => {
+                        res[keys::CONST_PARAM].insert(AstPtr::new(&a), 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(AstPtr::new(&ast_param), id);
             }
         }
     }
@@ -246,7 +258,7 @@ fn insert_item_loc<ID, N, Data>(
 {
     let loc = id.lookup(db);
     if loc.item_tree_id().file_id() == file_id {
-        res[key].insert(loc.source(db).value, id)
+        res[key].insert(loc.ast_ptr(db).value, id)
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs
index f83ab1e1a05..9d330a7bf1c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs
@@ -13,7 +13,7 @@ use crate::{
     TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId,
 };
 
-pub type Key<K, V> = crate::dyn_map::Key<K, V, AstPtrPolicy<K, V>>;
+pub type Key<K, V> = crate::dyn_map::Key<AstPtr<K>, V, AstPtrPolicy<K, V>>;
 
 pub const BLOCK: Key<ast::BlockExpr, BlockId> = Key::new();
 pub const FUNCTION: Key<ast::Fn, FunctionId> = Key::new();
@@ -39,6 +39,7 @@ pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new();
 pub const MACRO_RULES: Key<ast::MacroRules, MacroRulesId> = Key::new();
 pub const MACRO2: Key<ast::MacroDef, Macro2Id> = Key::new();
 pub const PROC_MACRO: Key<ast::Fn, ProcMacroId> = Key::new();
+pub const MACRO_CALL: Key<ast::MacroCall, MacroCallId> = Key::new();
 pub const ATTR_MACRO_CALL: Key<ast::Item, MacroCallId> = Key::new();
 pub const DERIVE_MACRO_CALL: Key<ast::Attr, (AttrId, MacroCallId, Box<[Option<MacroCallId>]>)> =
     Key::new();
@@ -54,18 +55,16 @@ pub struct AstPtrPolicy<AST, ID> {
 }
 
 impl<AST: AstNode + 'static, ID: 'static> Policy for AstPtrPolicy<AST, ID> {
-    type K = AST;
+    type K = AstPtr<AST>;
     type V = ID;
-    fn insert(map: &mut DynMap, key: AST, value: ID) {
-        let key = AstPtr::new(&key);
+    fn insert(map: &mut DynMap, key: AstPtr<AST>, value: ID) {
         map.map
             .entry::<FxHashMap<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: &AstPtr<AST>) -> Option<&'a ID> {
+        map.map.get::<FxHashMap<AstPtr<AST>, ID>>()?.get(key)
     }
     fn is_empty(map: &DynMap) -> bool {
         map.map.get::<FxHashMap<AstPtr<AST>, ID>>().map_or(true, |it| it.is_empty())
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
index ba96ab6cc2f..02fd431e4e7 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
@@ -67,6 +67,10 @@ impl BuiltinFnLikeExpander {
         let span = span_with_def_site_ctxt(db, span, id);
         self.expander()(db, id, tt, span)
     }
+
+    pub fn is_asm(&self) -> bool {
+        matches!(self, Self::Asm | Self::GlobalAsm)
+    }
 }
 
 impl EagerExpander {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
index 1ba85c5c7ea..743fac50f4e 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
@@ -1,6 +1,4 @@
 //! Things to wrap other things in file ids.
-use std::iter;
-
 use either::Either;
 use span::{
     AstIdNode, ErasedFileAstId, FileAstId, FileId, FileRange, HirFileId, HirFileIdRepr,
@@ -150,27 +148,16 @@ impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, N> {
     }
 }
 
+impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, &N> {
+    // unfortunately `syntax` collides with the impl above, because `&_` is fundamental
+    pub fn syntax_ref(&self) -> InFileWrapper<FileId, &SyntaxNode> {
+        self.with_value(self.value.syntax())
+    }
+}
+
 // region:specific impls
 
 impl InFile<&SyntaxNode> {
-    /// Traverse up macro calls and skips the macro invocation node
-    pub fn ancestors_with_macros(
-        self,
-        db: &dyn db::ExpandDatabase,
-    ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
-        let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
-            Some(parent) => Some(node.with_value(parent)),
-            None => db
-                .lookup_intern_macro_call(node.file_id.macro_file()?.macro_call_id)
-                .to_node_item(db)
-                .syntax()
-                .cloned()
-                .map(|node| node.parent())
-                .transpose(),
-        };
-        iter::successors(succ(&self.cloned()), succ)
-    }
-
     /// Falls back to the macro call range if the node cannot be mapped up fully.
     ///
     /// For attributes and derives, this will point back to the attribute only.
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index b34649d972f..131625a96aa 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -47,7 +47,7 @@ use crate::{
     builtin_attr_macro::BuiltinAttrExpander,
     builtin_derive_macro::BuiltinDeriveExpander,
     builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
-    db::{ExpandDatabase, TokenExpander},
+    db::ExpandDatabase,
     mod_path::ModPath,
     proc_macro::{CustomProcMacroExpander, ProcMacroKind},
     span_map::{ExpansionSpanMap, SpanMap},
@@ -253,9 +253,6 @@ pub trait HirFileIdExt {
     /// If this is a macro call, returns the syntax node of the very first macro call this file resides in.
     fn original_call_node(self, db: &dyn ExpandDatabase) -> Option<InRealFile<SyntaxNode>>;
 
-    /// Return expansion information if it is a macro-expansion file
-    fn expansion_info(self, db: &dyn ExpandDatabase) -> Option<ExpansionInfo>;
-
     fn as_builtin_derive_attr_node(&self, db: &dyn ExpandDatabase) -> Option<InFile<ast::Attr>>;
 }
 
@@ -309,11 +306,6 @@ impl HirFileIdExt for HirFileId {
         }
     }
 
-    /// Return expansion information if it is a macro-expansion file
-    fn expansion_info(self, db: &dyn ExpandDatabase) -> Option<ExpansionInfo> {
-        Some(ExpansionInfo::new(db, self.macro_file()?))
-    }
-
     fn as_builtin_derive_attr_node(&self, db: &dyn ExpandDatabase) -> Option<InFile<ast::Attr>> {
         let macro_file = self.macro_file()?;
         let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
@@ -417,8 +409,10 @@ impl MacroFileIdExt for MacroFileId {
     }
 
     fn is_attr_macro(&self, db: &dyn ExpandDatabase) -> bool {
-        let loc = db.lookup_intern_macro_call(self.macro_call_id);
-        matches!(loc.kind, MacroCallKind::Attr { .. })
+        matches!(
+            db.lookup_intern_macro_call(self.macro_call_id).def.kind,
+            MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, ProcMacroKind::Attr, _)
+        )
     }
 
     fn is_derive_attr_pseudo_expansion(&self, db: &dyn ExpandDatabase) -> bool {
@@ -703,16 +697,12 @@ impl MacroCallKind {
 // simpler function calls if the map is only used once
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct ExpansionInfo {
-    pub expanded: InMacroFile<SyntaxNode>,
+    expanded: InMacroFile<SyntaxNode>,
     /// The argument TokenTree or item for attributes
     arg: InFile<Option<SyntaxNode>>,
-    /// The `macro_rules!` or attribute input.
-    attr_input_or_mac_def: Option<InFile<ast::TokenTree>>,
-
-    macro_def: TokenExpander,
-    macro_arg: Arc<tt::Subtree>,
-    pub exp_map: Arc<ExpansionSpanMap>,
+    exp_map: Arc<ExpansionSpanMap>,
     arg_map: SpanMap,
+    loc: MacroCallLoc,
 }
 
 impl ExpansionInfo {
@@ -720,14 +710,21 @@ impl ExpansionInfo {
         self.expanded.clone()
     }
 
-    pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {
-        Some(self.arg.with_value(self.arg.value.as_ref()?.parent()?))
+    pub fn call_node(&self) -> InFile<Option<SyntaxNode>> {
+        self.arg.with_value(self.arg.value.as_ref().and_then(SyntaxNode::parent))
     }
 
     pub fn call_file(&self) -> HirFileId {
         self.arg.file_id
     }
 
+    pub fn is_attr(&self) -> bool {
+        matches!(
+            self.loc.def.kind,
+            MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, ProcMacroKind::Attr, _)
+        )
+    }
+
     /// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
     ///
     /// Note this does a linear search through the entire backing vector of the spanmap.
@@ -812,49 +809,15 @@ impl ExpansionInfo {
     }
 
     pub fn new(db: &dyn ExpandDatabase, macro_file: MacroFileId) -> ExpansionInfo {
-        let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
+        let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
 
         let arg_tt = loc.kind.arg(db);
         let arg_map = db.span_map(arg_tt.file_id);
 
-        let macro_def = db.macro_expander(loc.def);
         let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
         let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
 
-        let (macro_arg, _, _) =
-            db.macro_arg_considering_derives(macro_file.macro_call_id, &loc.kind);
-
-        let def = loc.def.ast_id().left().and_then(|id| {
-            let def_tt = match id.to_node(db) {
-                ast::Macro::MacroRules(mac) => mac.token_tree()?,
-                ast::Macro::MacroDef(_) if matches!(macro_def, TokenExpander::BuiltInAttr(_)) => {
-                    return None
-                }
-                ast::Macro::MacroDef(mac) => mac.body()?,
-            };
-            Some(InFile::new(id.file_id, def_tt))
-        });
-        let attr_input_or_mac_def = def.or_else(|| match loc.kind {
-            MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
-                // FIXME: handle `cfg_attr`
-                let tt = collect_attrs(&ast_id.to_node(db))
-                    .nth(invoc_attr_index.ast_index())
-                    .and_then(|x| Either::left(x.1))?
-                    .token_tree()?;
-                Some(InFile::new(ast_id.file_id, tt))
-            }
-            _ => None,
-        });
-
-        ExpansionInfo {
-            expanded,
-            arg: arg_tt,
-            attr_input_or_mac_def,
-            macro_arg,
-            macro_def,
-            exp_map,
-            arg_map,
-        }
+        ExpansionInfo { expanded, loc, arg: arg_tt, exp_map, arg_map }
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 0cde3f000a5..1eab509b7b1 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -19,8 +19,8 @@ use hir_def::{
     AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
 };
 use hir_expand::{
-    attrs::collect_attrs, db::ExpandDatabase, files::InRealFile, name::AsName, ExpansionInfo,
-    InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
+    attrs::collect_attrs, db::ExpandDatabase, files::InRealFile, name::AsName, InMacroFile,
+    MacroCallId, MacroFileId, MacroFileIdExt,
 };
 use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -129,12 +129,9 @@ pub struct Semantics<'db, DB> {
 
 pub struct SemanticsImpl<'db> {
     pub db: &'db dyn HirDatabase,
-    s2d_cache: RefCell<SourceToDefCache>,
+    s2d_cache: RefCell<(SourceToDefCache, FxHashMap<MacroFileId, hir_expand::ExpansionInfo>)>,
     /// Rootnode to HirFileId cache
     root_to_file_cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>,
-    // These 2 caches are mainly useful for semantic highlighting as nothing else descends a lot of tokens
-    // So we might wanna move them out into something specific for semantic highlighting
-    expansion_info_cache: RefCell<FxHashMap<MacroFileId, ExpansionInfo>>,
     /// MacroCall to its expansion's MacroFileId cache
     macro_call_cache: RefCell<FxHashMap<InFile<ast::MacroCall>, MacroFileId>>,
 }
@@ -295,7 +292,6 @@ impl<'db> SemanticsImpl<'db> {
             db,
             s2d_cache: Default::default(),
             root_to_file_cache: Default::default(),
-            expansion_info_cache: Default::default(),
             macro_call_cache: Default::default(),
         }
     }
@@ -314,7 +310,16 @@ impl<'db> SemanticsImpl<'db> {
 
     pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
         let sa = self.analyze_no_infer(macro_call.syntax())?;
-        let file_id = sa.expand(self.db, InFile::new(sa.file_id, macro_call))?;
+
+        let macro_call = InFile::new(sa.file_id, macro_call);
+        let file_id = if let Some(call) =
+            <ast::MacroCall as crate::semantics::ToDef>::to_def(self, macro_call)
+        {
+            call.as_macro_file()
+        } else {
+            sa.expand(self.db, macro_call)?
+        };
+
         let node = self.parse_or_expand(file_id.into());
         Some(node)
     }
@@ -322,7 +327,7 @@ impl<'db> SemanticsImpl<'db> {
     /// If `item` has an attribute macro attached to it, expands it.
     pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
         let src = self.wrap_node_infile(item.clone());
-        let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?;
+        let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src.as_ref()))?;
         Some(self.parse_or_expand(macro_call_id.as_file()))
     }
 
@@ -341,9 +346,7 @@ impl<'db> SemanticsImpl<'db> {
             Some(
                 calls
                     .into_iter()
-                    .map(|call| {
-                        macro_call_to_macro_id(ctx, self.db.upcast(), call?).map(|id| Macro { id })
-                    })
+                    .map(|call| macro_call_to_macro_id(ctx, call?).map(|id| Macro { id }))
                     .collect(),
             )
         })
@@ -403,7 +406,7 @@ impl<'db> SemanticsImpl<'db> {
 
     pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
         let file_id = self.find_file(item.syntax()).file_id;
-        let src = InFile::new(file_id, item.clone());
+        let src = InFile::new(file_id, item);
         self.with_ctx(|ctx| ctx.item_to_macro_call(src).is_some())
     }
 
@@ -453,7 +456,7 @@ impl<'db> SemanticsImpl<'db> {
         token_to_map: SyntaxToken,
     ) -> Option<(SyntaxNode, SyntaxToken)> {
         let macro_call = self.wrap_node_infile(actual_macro_call.clone());
-        let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(macro_call))?;
+        let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(macro_call.as_ref()))?;
         hir_expand::db::expand_speculative(
             self.db.upcast(),
             macro_call_id,
@@ -705,8 +708,6 @@ impl<'db> SemanticsImpl<'db> {
         let parent = token.parent()?;
         let file_id = self.find_file(&parent).file_id.file_id()?;
 
-        let mut cache = self.expansion_info_cache.borrow_mut();
-
         // iterate related crates and find all include! invocations that include_file_id matches
         for (invoc, _) in self
             .db
@@ -716,18 +717,31 @@ impl<'db> SemanticsImpl<'db> {
             .filter(|&(_, include_file_id)| include_file_id == file_id)
         {
             let macro_file = invoc.as_macro_file();
-            let expansion_info = cache.entry(macro_file).or_insert_with(|| {
-                let exp_info = macro_file.expansion_info(self.db.upcast());
-
-                let InMacroFile { file_id, value } = exp_info.expanded();
-                self.cache(value, file_id.into());
+            let expansion_info = {
+                self.with_ctx(|ctx| {
+                    ctx.expansion_info_cache
+                        .entry(macro_file)
+                        .or_insert_with(|| {
+                            let exp_info = macro_file.expansion_info(self.db.upcast());
+
+                            let InMacroFile { file_id, value } = exp_info.expanded();
+                            if let InFile { file_id, value: Some(value) } = exp_info.call_node() {
+                                self.cache(value.ancestors().last().unwrap(), file_id);
+                            }
+                            self.cache(value, file_id.into());
 
-                exp_info
-            });
+                            exp_info
+                        })
+                        .clone()
+                })
+            };
 
             // FIXME: uncached parse
             // Create the source analyzer for the macro call scope
-            let Some(sa) = self.analyze_no_infer(&self.parse_or_expand(expansion_info.call_file()))
+            let Some(sa) = expansion_info
+                .call_node()
+                .value
+                .and_then(|it| self.analyze_no_infer(&it.ancestors().last().unwrap()))
             else {
                 continue;
             };
@@ -785,23 +799,27 @@ impl<'db> SemanticsImpl<'db> {
                 }
             };
 
-        let mut cache = self.expansion_info_cache.borrow_mut();
-        let mut mcache = self.macro_call_cache.borrow_mut();
+        let mut m_cache = self.macro_call_cache.borrow_mut();
         let def_map = sa.resolver.def_map();
 
         let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![(file_id, smallvec![token])];
-        let mut process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
-            let exp_info = cache.entry(macro_file).or_insert_with(|| {
-                let exp_info = macro_file.expansion_info(self.db.upcast());
-
-                let InMacroFile { file_id, value } = exp_info.expanded();
-                self.cache(value, file_id.into());
-
-                exp_info
-            });
-
-            let InMacroFile { file_id, value: mapped_tokens } = exp_info.map_range_down(span)?;
-            let mapped_tokens: SmallVec<[_; 2]> = mapped_tokens.collect();
+        let process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
+            let InMacroFile { file_id, value: mapped_tokens } = self.with_ctx(|ctx| {
+                Some(
+                    ctx.expansion_info_cache
+                        .entry(macro_file)
+                        .or_insert_with(|| {
+                            let exp_info = macro_file.expansion_info(self.db.upcast());
+
+                            let InMacroFile { file_id, value } = exp_info.expanded();
+                            self.cache(value, file_id.into());
+
+                            exp_info
+                        })
+                        .map_range_down(span)?
+                        .map(SmallVec::<[_; 2]>::from_iter),
+                )
+            })?;
 
             // we have found a mapping for the token if the vec is non-empty
             let res = mapped_tokens.is_empty().not().then_some(());
@@ -818,10 +836,7 @@ impl<'db> SemanticsImpl<'db> {
                         token.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| {
                             // Don't force populate the dyn cache for items that don't have an attribute anyways
                             item.attrs().next()?;
-                            Some((
-                                ctx.item_to_macro_call(InFile::new(file_id, item.clone()))?,
-                                item,
-                            ))
+                            Some((ctx.item_to_macro_call(InFile::new(file_id, &item))?, item))
                         })
                     });
                     if let Some((call_id, item)) = containing_attribute_macro_call {
@@ -874,13 +889,20 @@ impl<'db> SemanticsImpl<'db> {
                                 return None;
                             }
                             let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
-                            let mcall: hir_expand::files::InFileWrapper<HirFileId, ast::MacroCall> =
-                                InFile::new(file_id, macro_call);
-                            let file_id = match mcache.get(&mcall) {
+                            let mcall = InFile::new(file_id, macro_call);
+                            let file_id = match m_cache.get(&mcall) {
                                 Some(&it) => it,
                                 None => {
-                                    let it = sa.expand(self.db, mcall.as_ref())?;
-                                    mcache.insert(mcall, it);
+                                    let it = if let Some(call) =
+                                        <ast::MacroCall as crate::semantics::ToDef>::to_def(
+                                            self,
+                                            mcall.as_ref(),
+                                        ) {
+                                        call.as_macro_file()
+                                    } else {
+                                        sa.expand(self.db, mcall.as_ref())?
+                                    };
+                                    m_cache.insert(mcall, it);
                                     it
                                 }
                             };
@@ -1056,16 +1078,19 @@ impl<'db> SemanticsImpl<'db> {
         node: SyntaxNode,
     ) -> impl Iterator<Item = SyntaxNode> + Clone + '_ {
         let node = self.find_file(&node);
-        let db = self.db.upcast();
         iter::successors(Some(node.cloned()), move |&InFile { file_id, ref value }| {
             match value.parent() {
                 Some(parent) => Some(InFile::new(file_id, parent)),
                 None => {
-                    let call_node = file_id.macro_file()?.call_node(db);
-                    // cache the node
-                    // FIXME: uncached parse
-                    self.parse_or_expand(call_node.file_id);
-                    Some(call_node)
+                    let macro_file = file_id.macro_file()?;
+
+                    self.with_ctx(|ctx| {
+                        let expansion_info = ctx
+                            .expansion_info_cache
+                            .entry(macro_file)
+                            .or_insert_with(|| macro_file.expansion_info(self.db.upcast()));
+                        expansion_info.call_node().transpose()
+                    })
                 }
             }
         })
@@ -1090,7 +1115,7 @@ impl<'db> SemanticsImpl<'db> {
                 .find(|tp| tp.lifetime().as_ref().map(|lt| lt.text()).as_ref() == Some(&text))
         })?;
         let src = self.wrap_node_infile(lifetime_param);
-        ToDef::to_def(self, src)
+        ToDef::to_def(self, src.as_ref())
     }
 
     pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
@@ -1112,7 +1137,7 @@ impl<'db> SemanticsImpl<'db> {
             })
         })?;
         let src = self.wrap_node_infile(label);
-        ToDef::to_def(self, src)
+        ToDef::to_def(self, src.as_ref())
     }
 
     pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
@@ -1308,8 +1333,8 @@ impl<'db> SemanticsImpl<'db> {
     pub fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<Macro> {
         let item_in_file = self.wrap_node_infile(item.clone());
         let id = self.with_ctx(|ctx| {
-            let macro_call_id = ctx.item_to_macro_call(item_in_file)?;
-            macro_call_to_macro_id(ctx, self.db.upcast(), macro_call_id)
+            let macro_call_id = ctx.item_to_macro_call(item_in_file.as_ref())?;
+            macro_call_to_macro_id(ctx, macro_call_id)
         })?;
         Some(Macro { id })
     }
@@ -1339,13 +1364,13 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     fn with_ctx<F: FnOnce(&mut SourceToDefCtx<'_, '_>) -> T, T>(&self, f: F) -> T {
-        let mut cache = self.s2d_cache.borrow_mut();
-        let mut ctx = SourceToDefCtx { db: self.db, dynmap_cache: &mut cache };
+        let (dynmap_cache, expansion_info_cache) = &mut *self.s2d_cache.borrow_mut();
+        let mut ctx = SourceToDefCtx { db: self.db, dynmap_cache, expansion_info_cache };
         f(&mut ctx)
     }
 
     pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
-        let src = self.find_file(src.syntax()).with_value(src).cloned();
+        let src = self.find_file(src.syntax()).with_value(src);
         T::to_def(self, src)
     }
 
@@ -1613,27 +1638,57 @@ impl<'db> SemanticsImpl<'db> {
 
 fn macro_call_to_macro_id(
     ctx: &mut SourceToDefCtx<'_, '_>,
-    db: &dyn ExpandDatabase,
     macro_call_id: MacroCallId,
 ) -> Option<MacroId> {
+    use span::HirFileIdRepr;
+
+    let db: &dyn ExpandDatabase = ctx.db.upcast();
     let loc = db.lookup_intern_macro_call(macro_call_id);
+
     match loc.def.ast_id() {
-        Either::Left(it) => ctx.macro_to_def(InFile::new(it.file_id, it.to_node(db))),
-        Either::Right(it) => ctx.proc_macro_to_def(InFile::new(it.file_id, it.to_node(db))),
+        Either::Left(it) => {
+            let node = match it.file_id.repr() {
+                HirFileIdRepr::FileId(file_id) => {
+                    it.to_ptr(db).to_node(&db.parse(file_id).syntax_node())
+                }
+                HirFileIdRepr::MacroFile(macro_file) => {
+                    let expansion_info = ctx
+                        .expansion_info_cache
+                        .entry(macro_file)
+                        .or_insert_with(|| macro_file.expansion_info(ctx.db.upcast()));
+                    it.to_ptr(db).to_node(&expansion_info.expanded().value)
+                }
+            };
+            ctx.macro_to_def(InFile::new(it.file_id, &node))
+        }
+        Either::Right(it) => {
+            let node = match it.file_id.repr() {
+                HirFileIdRepr::FileId(file_id) => {
+                    it.to_ptr(db).to_node(&db.parse(file_id).syntax_node())
+                }
+                HirFileIdRepr::MacroFile(macro_file) => {
+                    let expansion_info = ctx
+                        .expansion_info_cache
+                        .entry(macro_file)
+                        .or_insert_with(|| macro_file.expansion_info(ctx.db.upcast()));
+                    it.to_ptr(db).to_node(&expansion_info.expanded().value)
+                }
+            };
+            ctx.proc_macro_to_def(InFile::new(it.file_id, &node))
+        }
     }
 }
 
 pub trait ToDef: AstNode + Clone {
     type Def;
-
-    fn to_def(sema: &SemanticsImpl<'_>, src: InFile<Self>) -> Option<Self::Def>;
+    fn to_def(sema: &SemanticsImpl<'_>, src: InFile<&Self>) -> Option<Self::Def>;
 }
 
 macro_rules! to_def_impls {
     ($(($def:path, $ast:path, $meth:ident)),* ,) => {$(
         impl ToDef for $ast {
             type Def = $def;
-            fn to_def(sema: &SemanticsImpl<'_>, src: InFile<Self>) -> Option<Self::Def> {
+            fn to_def(sema: &SemanticsImpl<'_>, src: InFile<&Self>) -> Option<Self::Def> {
                 sema.with_ctx(|ctx| ctx.$meth(src)).map(<$def>::from)
             }
         }
@@ -1666,6 +1721,7 @@ to_def_impls![
     (crate::Label, ast::Label, label_to_def),
     (crate::Adt, ast::Adt, adt_to_def),
     (crate::ExternCrateDecl, ast::ExternCrate, extern_crate_to_def),
+    (MacroCallId, ast::MacroCall, macro_call_to_macro_call),
 ];
 
 fn find_root(node: &SyntaxNode) -> SyntaxNode {
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
index 77e7cdb58ab..5f66bdc0434 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
@@ -98,22 +98,26 @@ use hir_def::{
     FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId,
     StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, UseId, VariantId,
 };
-use hir_expand::{attrs::AttrId, name::AsName, HirFileId, HirFileIdExt, MacroCallId};
+use hir_expand::{
+    attrs::AttrId, name::AsName, ExpansionInfo, HirFileId, HirFileIdExt, MacroCallId,
+};
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
+use span::MacroFileId;
 use stdx::impl_from;
 use syntax::{
     ast::{self, HasName},
-    AstNode, SyntaxNode,
+    AstNode, AstPtr, SyntaxNode,
 };
 
 use crate::{db::HirDatabase, InFile};
 
 pub(super) type SourceToDefCache = FxHashMap<(ChildContainer, HirFileId), DynMap>;
 
-pub(super) struct SourceToDefCtx<'a, 'b> {
-    pub(super) db: &'b dyn HirDatabase,
-    pub(super) dynmap_cache: &'a mut SourceToDefCache,
+pub(super) struct SourceToDefCtx<'a, 'dyn_cache> {
+    pub(super) db: &'a dyn HirDatabase,
+    pub(super) dynmap_cache: &'dyn_cache mut SourceToDefCache,
+    pub(super) expansion_info_cache: &'a mut FxHashMap<MacroFileId, ExpansionInfo>,
 }
 
 impl SourceToDefCtx<'_, '_> {
@@ -135,19 +139,21 @@ impl SourceToDefCtx<'_, '_> {
         mods
     }
 
-    pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> {
+    pub(super) fn module_to_def(&mut self, src: InFile<&ast::Module>) -> Option<ModuleId> {
         let _p = tracing::span!(tracing::Level::INFO, "module_to_def").entered();
-        let parent_declaration = src
-            .syntax()
-            .ancestors_with_macros(self.db.upcast())
-            .find_map(|it| it.map(Either::<ast::Module, ast::BlockExpr>::cast).transpose())
+        let parent_declaration = self
+            .ancestors_with_macros(src.syntax_ref(), |_, ancestor| {
+                ancestor.map(Either::<ast::Module, ast::BlockExpr>::cast).transpose()
+            })
             .map(|it| it.transpose());
 
         let parent_module = match parent_declaration {
             Some(Either::Right(parent_block)) => self
-                .block_to_def(parent_block)
+                .block_to_def(parent_block.as_ref())
                 .map(|block| self.db.block_def_map(block).root_module_id()),
-            Some(Either::Left(parent_declaration)) => self.module_to_def(parent_declaration),
+            Some(Either::Left(parent_declaration)) => {
+                self.module_to_def(parent_declaration.as_ref())
+            }
             None => {
                 let file_id = src.file_id.original_file(self.db.upcast());
                 self.file_to_def(file_id).first().copied()
@@ -160,73 +166,79 @@ impl SourceToDefCtx<'_, '_> {
         Some(def_map.module_id(child_id))
     }
 
-    pub(super) fn source_file_to_def(&self, src: InFile<ast::SourceFile>) -> Option<ModuleId> {
+    pub(super) fn source_file_to_def(&self, src: InFile<&ast::SourceFile>) -> Option<ModuleId> {
         let _p = tracing::span!(tracing::Level::INFO, "source_file_to_def").entered();
         let file_id = src.file_id.original_file(self.db.upcast());
         self.file_to_def(file_id).first().copied()
     }
 
-    pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> {
+    pub(super) fn trait_to_def(&mut self, src: InFile<&ast::Trait>) -> Option<TraitId> {
         self.to_def(src, keys::TRAIT)
     }
     pub(super) fn trait_alias_to_def(
         &mut self,
-        src: InFile<ast::TraitAlias>,
+        src: InFile<&ast::TraitAlias>,
     ) -> Option<TraitAliasId> {
         self.to_def(src, keys::TRAIT_ALIAS)
     }
-    pub(super) fn impl_to_def(&mut self, src: InFile<ast::Impl>) -> Option<ImplId> {
+    pub(super) fn impl_to_def(&mut self, src: InFile<&ast::Impl>) -> Option<ImplId> {
         self.to_def(src, keys::IMPL)
     }
-    pub(super) fn fn_to_def(&mut self, src: InFile<ast::Fn>) -> Option<FunctionId> {
+    pub(super) fn fn_to_def(&mut self, src: InFile<&ast::Fn>) -> Option<FunctionId> {
         self.to_def(src, keys::FUNCTION)
     }
-    pub(super) fn struct_to_def(&mut self, src: InFile<ast::Struct>) -> Option<StructId> {
+    pub(super) fn struct_to_def(&mut self, src: InFile<&ast::Struct>) -> Option<StructId> {
         self.to_def(src, keys::STRUCT)
     }
-    pub(super) fn enum_to_def(&mut self, src: InFile<ast::Enum>) -> Option<EnumId> {
+    pub(super) fn enum_to_def(&mut self, src: InFile<&ast::Enum>) -> Option<EnumId> {
         self.to_def(src, keys::ENUM)
     }
-    pub(super) fn union_to_def(&mut self, src: InFile<ast::Union>) -> Option<UnionId> {
+    pub(super) fn union_to_def(&mut self, src: InFile<&ast::Union>) -> Option<UnionId> {
         self.to_def(src, keys::UNION)
     }
-    pub(super) fn static_to_def(&mut self, src: InFile<ast::Static>) -> Option<StaticId> {
+    pub(super) fn static_to_def(&mut self, src: InFile<&ast::Static>) -> Option<StaticId> {
         self.to_def(src, keys::STATIC)
     }
-    pub(super) fn const_to_def(&mut self, src: InFile<ast::Const>) -> Option<ConstId> {
+    pub(super) fn const_to_def(&mut self, src: InFile<&ast::Const>) -> Option<ConstId> {
         self.to_def(src, keys::CONST)
     }
-    pub(super) fn type_alias_to_def(&mut self, src: InFile<ast::TypeAlias>) -> Option<TypeAliasId> {
+    pub(super) fn type_alias_to_def(
+        &mut self,
+        src: InFile<&ast::TypeAlias>,
+    ) -> Option<TypeAliasId> {
         self.to_def(src, keys::TYPE_ALIAS)
     }
-    pub(super) fn record_field_to_def(&mut self, src: InFile<ast::RecordField>) -> Option<FieldId> {
+    pub(super) fn record_field_to_def(
+        &mut self,
+        src: InFile<&ast::RecordField>,
+    ) -> Option<FieldId> {
         self.to_def(src, keys::RECORD_FIELD)
     }
-    pub(super) fn tuple_field_to_def(&mut self, src: InFile<ast::TupleField>) -> Option<FieldId> {
+    pub(super) fn tuple_field_to_def(&mut self, src: InFile<&ast::TupleField>) -> Option<FieldId> {
         self.to_def(src, keys::TUPLE_FIELD)
     }
-    pub(super) fn block_to_def(&mut self, src: InFile<ast::BlockExpr>) -> Option<BlockId> {
+    pub(super) fn block_to_def(&mut self, src: InFile<&ast::BlockExpr>) -> Option<BlockId> {
         self.to_def(src, keys::BLOCK)
     }
     pub(super) fn enum_variant_to_def(
         &mut self,
-        src: InFile<ast::Variant>,
+        src: InFile<&ast::Variant>,
     ) -> Option<EnumVariantId> {
         self.to_def(src, keys::ENUM_VARIANT)
     }
     pub(super) fn extern_crate_to_def(
         &mut self,
-        src: InFile<ast::ExternCrate>,
+        src: InFile<&ast::ExternCrate>,
     ) -> Option<ExternCrateId> {
         self.to_def(src, keys::EXTERN_CRATE)
     }
     #[allow(dead_code)]
-    pub(super) fn use_to_def(&mut self, src: InFile<ast::Use>) -> Option<UseId> {
+    pub(super) fn use_to_def(&mut self, src: InFile<&ast::Use>) -> Option<UseId> {
         self.to_def(src, keys::USE)
     }
     pub(super) fn adt_to_def(
         &mut self,
-        InFile { file_id, value }: InFile<ast::Adt>,
+        InFile { file_id, value }: InFile<&ast::Adt>,
     ) -> Option<AdtId> {
         match value {
             ast::Adt::Enum(it) => self.enum_to_def(InFile::new(file_id, it)).map(AdtId::EnumId),
@@ -238,11 +250,11 @@ impl SourceToDefCtx<'_, '_> {
     }
     pub(super) fn bind_pat_to_def(
         &mut self,
-        src: InFile<ast::IdentPat>,
+        src: InFile<&ast::IdentPat>,
     ) -> Option<(DefWithBodyId, BindingId)> {
-        let container = self.find_pat_or_label_container(src.syntax())?;
+        let container = self.find_pat_or_label_container(src.syntax_ref())?;
         let (body, source_map) = self.db.body_with_source_map(container);
-        let src = src.map(ast::Pat::from);
+        let src = src.cloned().map(ast::Pat::from);
         let pat_id = source_map.node_pat(src.as_ref())?;
         // the pattern could resolve to a constant, verify that that is not the case
         if let crate::Pat::Bind { id, .. } = body[pat_id] {
@@ -253,25 +265,33 @@ impl SourceToDefCtx<'_, '_> {
     }
     pub(super) fn self_param_to_def(
         &mut self,
-        src: InFile<ast::SelfParam>,
+        src: InFile<&ast::SelfParam>,
     ) -> Option<(DefWithBodyId, BindingId)> {
-        let container = self.find_pat_or_label_container(src.syntax())?;
+        let container = self.find_pat_or_label_container(src.syntax_ref())?;
         let body = self.db.body(container);
         Some((container, body.self_param?))
     }
     pub(super) fn label_to_def(
         &mut self,
-        src: InFile<ast::Label>,
+        src: InFile<&ast::Label>,
     ) -> Option<(DefWithBodyId, LabelId)> {
-        let container = self.find_pat_or_label_container(src.syntax())?;
+        let container = self.find_pat_or_label_container(src.syntax_ref())?;
         let (_body, source_map) = self.db.body_with_source_map(container);
-        let label_id = source_map.node_label(src.as_ref())?;
+        let label_id = source_map.node_label(src)?;
         Some((container, label_id))
     }
 
-    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()
+    pub(super) fn item_to_macro_call(&mut self, src: InFile<&ast::Item>) -> Option<MacroCallId> {
+        let map = self.dyn_map(src)?;
+        map[keys::ATTR_MACRO_CALL].get(&AstPtr::new(src.value)).copied()
+    }
+
+    pub(super) fn macro_call_to_macro_call(
+        &mut self,
+        src: InFile<&ast::MacroCall>,
+    ) -> Option<MacroCallId> {
+        let map = self.dyn_map(src)?;
+        map[keys::MACRO_CALL].get(&AstPtr::new(src.value)).copied()
     }
 
     /// (AttrId, derive attribute call id, derive call ids)
@@ -282,7 +302,7 @@ impl SourceToDefCtx<'_, '_> {
     ) -> Option<(AttrId, MacroCallId, &[Option<MacroCallId>])> {
         let map = self.dyn_map(item)?;
         map[keys::DERIVE_MACRO_CALL]
-            .get(&src.value)
+            .get(&AstPtr::new(&src.value))
             .map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids))
     }
 
@@ -292,10 +312,10 @@ impl SourceToDefCtx<'_, '_> {
 
     fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
         &mut self,
-        src: InFile<Ast>,
+        src: InFile<&Ast>,
         key: Key<Ast, ID>,
     ) -> Option<ID> {
-        self.dyn_map(src.as_ref())?[key].get(&src.value).copied()
+        self.dyn_map(src)?[key].get(&AstPtr::new(src.value)).copied()
     }
 
     fn dyn_map<Ast: AstNode + 'static>(&mut self, src: InFile<&Ast>) -> Option<&DynMap> {
@@ -310,33 +330,42 @@ impl SourceToDefCtx<'_, '_> {
             .or_insert_with(|| container.child_by_source(db, file_id))
     }
 
-    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();
+    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_ref())?.into();
         let dyn_map = self.cache_for(container, src.file_id);
-        dyn_map[keys::TYPE_PARAM].get(&src.value).copied().map(TypeParamId::from_unchecked)
+        dyn_map[keys::TYPE_PARAM]
+            .get(&AstPtr::new(src.value))
+            .copied()
+            .map(TypeParamId::from_unchecked)
     }
 
     pub(super) fn lifetime_param_to_def(
         &mut self,
-        src: InFile<ast::LifetimeParam>,
+        src: InFile<&ast::LifetimeParam>,
     ) -> Option<LifetimeParamId> {
-        let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into();
+        let container: ChildContainer = self.find_generic_param_container(src.syntax_ref())?.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(&AstPtr::new(src.value)).copied()
     }
 
     pub(super) fn const_param_to_def(
         &mut self,
-        src: InFile<ast::ConstParam>,
+        src: InFile<&ast::ConstParam>,
     ) -> Option<ConstParamId> {
-        let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into();
+        let container: ChildContainer = self.find_generic_param_container(src.syntax_ref())?.into();
         let dyn_map = self.cache_for(container, src.file_id);
-        dyn_map[keys::CONST_PARAM].get(&src.value).copied().map(ConstParamId::from_unchecked)
+        dyn_map[keys::CONST_PARAM]
+            .get(&AstPtr::new(src.value))
+            .copied()
+            .map(ConstParamId::from_unchecked)
     }
 
     pub(super) fn generic_param_to_def(
         &mut self,
-        InFile { file_id, value }: InFile<ast::GenericParam>,
+        InFile { file_id, value }: InFile<&ast::GenericParam>,
     ) -> Option<GenericParamId> {
         match value {
             ast::GenericParam::ConstParam(it) => {
@@ -351,34 +380,111 @@ impl SourceToDefCtx<'_, '_> {
         }
     }
 
-    pub(super) fn macro_to_def(&mut self, src: InFile<ast::Macro>) -> Option<MacroId> {
-        self.dyn_map(src.as_ref()).and_then(|it| match &src.value {
+    pub(super) fn macro_to_def(&mut self, src: InFile<&ast::Macro>) -> Option<MacroId> {
+        self.dyn_map(src).and_then(|it| match src.value {
             ast::Macro::MacroRules(value) => {
-                it[keys::MACRO_RULES].get(value).copied().map(MacroId::from)
+                it[keys::MACRO_RULES].get(&AstPtr::new(value)).copied().map(MacroId::from)
+            }
+            ast::Macro::MacroDef(value) => {
+                it[keys::MACRO2].get(&AstPtr::new(value)).copied().map(MacroId::from)
             }
-            ast::Macro::MacroDef(value) => it[keys::MACRO2].get(value).copied().map(MacroId::from),
         })
     }
 
-    pub(super) fn proc_macro_to_def(&mut self, src: InFile<ast::Fn>) -> Option<MacroId> {
-        self.dyn_map(src.as_ref())
-            .and_then(|it| it[keys::PROC_MACRO].get(&src.value).copied().map(MacroId::from))
+    pub(super) fn proc_macro_to_def(&mut self, src: InFile<&ast::Fn>) -> Option<MacroId> {
+        self.dyn_map(src).and_then(|it| {
+            it[keys::PROC_MACRO].get(&AstPtr::new(src.value)).copied().map(MacroId::from)
+        })
     }
 
     pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
-        for container in src.ancestors_with_macros(self.db.upcast()) {
-            if let Some(res) = self.container_to_def(container) {
-                return Some(res);
-            }
+        let def =
+            self.ancestors_with_macros(src, |this, container| this.container_to_def(container));
+        if let Some(def) = def {
+            return Some(def);
         }
 
         let def = self.file_to_def(src.file_id.original_file(self.db.upcast())).first().copied()?;
         Some(def.into())
     }
 
+    /// Skips the attributed item that caused the macro invocation we are climbing up
+    fn ancestors_with_macros<T>(
+        &mut self,
+        node: InFile<&SyntaxNode>,
+        mut cb: impl FnMut(&mut Self, InFile<SyntaxNode>) -> Option<T>,
+    ) -> Option<T> {
+        use hir_expand::MacroFileIdExt;
+        let parent = |this: &mut Self, node: InFile<&SyntaxNode>| match node.value.parent() {
+            Some(parent) => Some(node.with_value(parent)),
+            None => {
+                let macro_file = node.file_id.macro_file()?;
+
+                let expansion_info = this
+                    .expansion_info_cache
+                    .entry(macro_file)
+                    .or_insert_with(|| macro_file.expansion_info(this.db.upcast()));
+
+                expansion_info.call_node().map(|node| node?.parent()).transpose()
+            }
+        };
+        let mut node = node.cloned();
+        while let Some(parent) = parent(self, node.as_ref()) {
+            if let Some(res) = cb(self, parent.clone()) {
+                return Some(res);
+            }
+            node = parent;
+        }
+        None
+    }
+
+    fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> {
+        self.ancestors_with_macros(src, |this, InFile { file_id, value }| {
+            let item = ast::Item::cast(value)?;
+            match &item {
+                ast::Item::Fn(it) => this.fn_to_def(InFile::new(file_id, it)).map(Into::into),
+                ast::Item::Struct(it) => {
+                    this.struct_to_def(InFile::new(file_id, it)).map(Into::into)
+                }
+                ast::Item::Enum(it) => this.enum_to_def(InFile::new(file_id, it)).map(Into::into),
+                ast::Item::Trait(it) => this.trait_to_def(InFile::new(file_id, it)).map(Into::into),
+                ast::Item::TraitAlias(it) => {
+                    this.trait_alias_to_def(InFile::new(file_id, it)).map(Into::into)
+                }
+                ast::Item::TypeAlias(it) => {
+                    this.type_alias_to_def(InFile::new(file_id, it)).map(Into::into)
+                }
+                ast::Item::Impl(it) => this.impl_to_def(InFile::new(file_id, it)).map(Into::into),
+                _ => None,
+            }
+        })
+    }
+
+    fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> {
+        self.ancestors_with_macros(src, |this, InFile { file_id, value }| {
+            let item = match ast::Item::cast(value.clone()) {
+                Some(it) => it,
+                None => {
+                    let variant = ast::Variant::cast(value.clone())?;
+                    return this
+                        .enum_variant_to_def(InFile::new(file_id, &variant))
+                        .map(Into::into);
+                }
+            };
+            match &item {
+                ast::Item::Fn(it) => this.fn_to_def(InFile::new(file_id, it)).map(Into::into),
+                ast::Item::Const(it) => this.const_to_def(InFile::new(file_id, it)).map(Into::into),
+                ast::Item::Static(it) => {
+                    this.static_to_def(InFile::new(file_id, it)).map(Into::into)
+                }
+                _ => None,
+            }
+        })
+    }
+
     fn container_to_def(&mut self, container: InFile<SyntaxNode>) -> Option<ChildContainer> {
         let cont = if let Some(item) = ast::Item::cast(container.value.clone()) {
-            match item {
+            match &item {
                 ast::Item::Module(it) => self.module_to_def(container.with_value(it))?.into(),
                 ast::Item::Trait(it) => self.trait_to_def(container.with_value(it))?.into(),
                 ast::Item::TraitAlias(it) => {
@@ -413,63 +519,11 @@ impl SourceToDefCtx<'_, '_> {
             }
         } else {
             let it = ast::Variant::cast(container.value)?;
-            let def = self.enum_variant_to_def(InFile::new(container.file_id, it))?;
+            let def = self.enum_variant_to_def(InFile::new(container.file_id, &it))?;
             DefWithBodyId::from(def).into()
         };
         Some(cont)
     }
-
-    fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> {
-        let ancestors = src.ancestors_with_macros(self.db.upcast());
-        for InFile { file_id, value } in ancestors {
-            let item = match ast::Item::cast(value) {
-                Some(it) => it,
-                None => continue,
-            };
-            let res: GenericDefId = match item {
-                ast::Item::Fn(it) => self.fn_to_def(InFile::new(file_id, it))?.into(),
-                ast::Item::Struct(it) => self.struct_to_def(InFile::new(file_id, it))?.into(),
-                ast::Item::Union(it) => self.union_to_def(InFile::new(file_id, it))?.into(),
-                ast::Item::Enum(it) => self.enum_to_def(InFile::new(file_id, it))?.into(),
-                ast::Item::Trait(it) => self.trait_to_def(InFile::new(file_id, it))?.into(),
-                ast::Item::TraitAlias(it) => {
-                    self.trait_alias_to_def(InFile::new(file_id, it))?.into()
-                }
-                ast::Item::TypeAlias(it) => {
-                    self.type_alias_to_def(InFile::new(file_id, it))?.into()
-                }
-                ast::Item::Impl(it) => self.impl_to_def(InFile::new(file_id, it))?.into(),
-                _ => continue,
-            };
-            return Some(res);
-        }
-        None
-    }
-
-    fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> {
-        let ancestors = src.ancestors_with_macros(self.db.upcast());
-        for InFile { file_id, value } in ancestors {
-            let item = match ast::Item::cast(value.clone()) {
-                Some(it) => it,
-                None => {
-                    if let Some(variant) = ast::Variant::cast(value.clone()) {
-                        return self
-                            .enum_variant_to_def(InFile::new(file_id, variant))
-                            .map(Into::into);
-                    }
-                    continue;
-                }
-            };
-            let res: DefWithBodyId = match item {
-                ast::Item::Const(it) => self.const_to_def(InFile::new(file_id, it))?.into(),
-                ast::Item::Static(it) => self.static_to_def(InFile::new(file_id, it))?.into(),
-                ast::Item::Fn(it) => self.fn_to_def(InFile::new(file_id, it))?.into(),
-                _ => continue,
-            };
-            return Some(res);
-        }
-        None
-    }
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
@@ -501,6 +555,7 @@ impl_from! {
 
 impl ChildContainer {
     fn child_by_source(self, db: &dyn HirDatabase, file_id: HirFileId) -> DynMap {
+        let _p = tracing::span!(tracing::Level::INFO, "ChildContainer::child_by_source").entered();
         let db = db.upcast();
         match self {
             ChildContainer::DefWithBodyId(it) => it.child_by_source(db, file_id),
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index d2295840642..138f3d8604b 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -24,11 +24,12 @@ use hir_def::{
     LocalFieldId, Lookup, ModuleDefId, TraitId, VariantId,
 };
 use hir_expand::{
-    builtin_fn_macro::BuiltinFnLikeExpander,
     mod_path::path,
-    name,
-    name::{AsName, Name},
     HirFileId, InFile, InMacroFile, MacroFileId, MacroFileIdExt,
+    {
+        name,
+        name::{AsName, Name},
+    },
 };
 use hir_ty::{
     diagnostics::{
@@ -822,6 +823,8 @@ impl SourceAnalyzer {
         macro_call: InFile<&ast::MacroCall>,
     ) -> Option<MacroFileId> {
         let krate = self.resolver.krate();
+        // FIXME: This causes us to parse, generally this is the wrong approach for resolving a
+        // macro call to a macro call id!
         let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| {
             self.resolver.resolve_path_as_macro_def(db.upcast(), &path, Some(MacroSubNs::Bang))
         })?;
@@ -851,13 +854,8 @@ impl SourceAnalyzer {
                 hir_def::MacroId::MacroRulesId(it) => it.lookup(db.upcast()).expander,
                 _ => hir_def::MacroExpander::Declarative,
             };
-            match ex {
-                hir_def::MacroExpander::BuiltIn(e)
-                    if e == BuiltinFnLikeExpander::Asm || e == BuiltinFnLikeExpander::GlobalAsm =>
-                {
-                    return true
-                }
-                _ => (),
+            if matches!(ex, hir_def::MacroExpander::BuiltIn(ex) if ex.is_asm()) {
+                return true;
             }
         }
         let macro_expr = match macro_call
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index 5d617780b6d..4aa5e37f983 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -74,7 +74,7 @@ fn integrated_highlighting_benchmark() {
         host.apply_change(change);
     }
 
-    let _g = crate::tracing::hprof::init("*>20");
+    let _g = crate::tracing::hprof::init("*>10");
 
     {
         let _it = stdx::timeit("after change");