about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-05-13 15:16:46 +0000
committerbors <bors@rust-lang.org>2024-05-13 15:16:46 +0000
commitd891ec697aff547407f26c2c26802add5b6c81a3 (patch)
tree32a68734d4e5ee27e8fc443329576d79957cf918
parent6e0c33d1ba36f633c44a730e5de164aae079383b (diff)
parentf1aa040ef69160018c25b7db54e05111546af21b (diff)
Auto merge of #17221 - Veykril:lazier-validation, r=Veykril
internal: Lazier macro parse tree validation
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/lib.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs70
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs29
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs25
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/db.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/diagnostics.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs82
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs6
14 files changed, 151 insertions, 140 deletions
diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
index f8cb431c3cd..2c13eed56c3 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
@@ -8,7 +8,7 @@ mod input;
 use std::panic;
 
 use salsa::Durability;
-use syntax::{ast, Parse, SourceFile};
+use syntax::{ast, Parse, SourceFile, SyntaxError};
 use triomphe::Arc;
 
 pub use crate::{
@@ -62,6 +62,9 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug {
     /// Parses the file into the syntax tree.
     fn parse(&self, file_id: FileId) -> Parse<ast::SourceFile>;
 
+    /// Returns the set of errors obtained from parsing the file including validation errors.
+    fn parse_errors(&self, file_id: FileId) -> Option<Arc<[SyntaxError]>>;
+
     /// The crate graph.
     #[salsa::input]
     fn crate_graph(&self) -> Arc<CrateGraph>;
@@ -88,6 +91,14 @@ fn parse(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
     SourceFile::parse(&text, span::Edition::CURRENT)
 }
 
+fn parse_errors(db: &dyn SourceDatabase, file_id: FileId) -> Option<Arc<[SyntaxError]>> {
+    let errors = db.parse(file_id).errors();
+    match &*errors {
+        [] => None,
+        [..] => Some(errors.into()),
+    }
+}
+
 /// We don't want to give HIR knowledge of source roots, hence we extract these
 /// methods into a separate DB.
 #[salsa::query_group(SourceDatabaseExtStorage)]
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 c9f1add2751..d2f4d7b7e56 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -10,9 +10,10 @@ use std::ops::Index;
 
 use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
-use hir_expand::{name::Name, HirFileId, InFile};
+use hir_expand::{name::Name, InFile};
 use la_arena::{Arena, ArenaMap};
 use rustc_hash::FxHashMap;
+use span::MacroFileId;
 use syntax::{ast, AstPtr, SyntaxNodePtr};
 use triomphe::Arc;
 
@@ -98,7 +99,7 @@ pub struct BodySourceMap {
 
     format_args_template_map: FxHashMap<ExprId, Vec<(syntax::TextRange, Name)>>,
 
-    expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
+    expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, MacroFileId>,
 
     /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in
     /// the source map (since they're just as volatile).
@@ -349,11 +350,17 @@ impl BodySourceMap {
         self.expr_map.get(&src).cloned()
     }
 
-    pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId> {
+    pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<MacroFileId> {
         let src = node.map(AstPtr::new);
         self.expansions.get(&src).cloned()
     }
 
+    pub fn macro_calls(
+        &self,
+    ) -> impl Iterator<Item = (InFile<AstPtr<ast::MacroCall>>, MacroFileId)> + '_ {
+        self.expansions.iter().map(|(&a, &b)| (a, b))
+    }
+
     pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
         self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
     }
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 f58228c45f0..82f89393add 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
@@ -1006,7 +1006,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());
+                self.source_map
+                    .expansions
+                    .insert(macro_call_ptr, self.expander.current_file_id().macro_file().unwrap());
                 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/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index 1b77de575cf..51a4dd6f42a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -229,7 +229,7 @@ pub struct TraitData {
     /// method calls to this trait's methods when the receiver is an array and the crate edition is
     /// 2015 or 2018.
     // box it as the vec is usually empty anyways
-    pub attribute_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
+    pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
 }
 
 impl TraitData {
@@ -258,12 +258,12 @@ impl TraitData {
         let mut collector =
             AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr));
         collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items);
-        let (items, attribute_calls, diagnostics) = collector.finish();
+        let (items, macro_calls, diagnostics) = collector.finish();
 
         (
             Arc::new(TraitData {
                 name,
-                attribute_calls,
+                macro_calls,
                 items,
                 is_auto,
                 is_unsafe,
@@ -298,7 +298,7 @@ impl TraitData {
     }
 
     pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
-        self.attribute_calls.iter().flat_map(|it| it.iter()).copied()
+        self.macro_calls.iter().flat_map(|it| it.iter()).copied()
     }
 }
 
@@ -319,7 +319,7 @@ impl TraitAliasData {
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq)]
 pub struct ImplData {
     pub target_trait: Option<Interned<TraitRef>>,
     pub self_ty: Interned<TypeRef>,
@@ -327,7 +327,7 @@ pub struct ImplData {
     pub is_negative: bool,
     pub is_unsafe: bool,
     // box it as the vec is usually empty anyways
-    pub attribute_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
+    pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
 }
 
 impl ImplData {
@@ -354,7 +354,7 @@ impl ImplData {
             AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::ImplId(id));
         collector.collect(&item_tree, tree_id.tree_id(), &impl_def.items);
 
-        let (items, attribute_calls, diagnostics) = collector.finish();
+        let (items, macro_calls, diagnostics) = collector.finish();
         let items = items.into_iter().map(|(_, item)| item).collect();
 
         (
@@ -364,14 +364,14 @@ impl ImplData {
                 items,
                 is_negative,
                 is_unsafe,
-                attribute_calls,
+                macro_calls,
             }),
             DefDiagnostics::new(diagnostics),
         )
     }
 
     pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
-        self.attribute_calls.iter().flat_map(|it| it.iter()).copied()
+        self.macro_calls.iter().flat_map(|it| it.iter()).copied()
     }
 }
 
@@ -573,7 +573,7 @@ struct AssocItemCollector<'a> {
     expander: Expander,
 
     items: Vec<(Name, AssocItemId)>,
-    attr_calls: Vec<(AstId<ast::Item>, MacroCallId)>,
+    macro_calls: Vec<(AstId<ast::Item>, MacroCallId)>,
 }
 
 impl<'a> AssocItemCollector<'a> {
@@ -590,7 +590,7 @@ impl<'a> AssocItemCollector<'a> {
             container,
             expander: Expander::new(db, file_id, module_id),
             items: Vec::new(),
-            attr_calls: Vec::new(),
+            macro_calls: Vec::new(),
             diagnostics: Vec::new(),
         }
     }
@@ -604,7 +604,7 @@ impl<'a> AssocItemCollector<'a> {
     ) {
         (
             self.items,
-            if self.attr_calls.is_empty() { None } else { Some(Box::new(self.attr_calls)) },
+            if self.macro_calls.is_empty() { None } else { Some(Box::new(self.macro_calls)) },
             self.diagnostics,
         )
     }
@@ -662,11 +662,11 @@ impl<'a> AssocItemCollector<'a> {
                             }
                         }
 
-                        self.attr_calls.push((ast_id, call_id));
+                        self.macro_calls.push((ast_id, call_id));
 
                         let res =
                             self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
-                        self.collect_macro_items(res, &|| loc.kind.clone());
+                        self.collect_macro_items(res);
                         continue 'items;
                     }
                     Ok(_) => (),
@@ -743,11 +743,8 @@ impl<'a> AssocItemCollector<'a> {
                     Ok(Some(call_id)) => {
                         let res =
                             self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
-                        self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike {
-                            ast_id: InFile::new(file_id, ast_id),
-                            expand_to: hir_expand::ExpandTo::Items,
-                            eager: None,
-                        });
+                        self.macro_calls.push((InFile::new(file_id, ast_id.upcast()), call_id));
+                        self.collect_macro_items(res);
                     }
                     Ok(None) => (),
                     Err(_) => {
@@ -766,39 +763,8 @@ impl<'a> AssocItemCollector<'a> {
         }
     }
 
-    fn collect_macro_items(
-        &mut self,
-        ExpandResult { value, err }: ExpandResult<Option<(Mark, Parse<ast::MacroItems>)>>,
-        error_call_kind: &dyn Fn() -> hir_expand::MacroCallKind,
-    ) {
-        let Some((mark, parse)) = value else { return };
-
-        if let Some(err) = err {
-            let diag = match err {
-                // why is this reported here?
-                hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
-                    DefDiagnostic::unresolved_proc_macro(
-                        self.module_id.local_id,
-                        error_call_kind(),
-                        krate,
-                    )
-                }
-                _ => DefDiagnostic::macro_error(
-                    self.module_id.local_id,
-                    error_call_kind(),
-                    err.to_string(),
-                ),
-            };
-            self.diagnostics.push(diag);
-        }
-        let errors = parse.errors();
-        if !errors.is_empty() {
-            self.diagnostics.push(DefDiagnostic::macro_expansion_parse_error(
-                self.module_id.local_id,
-                error_call_kind(),
-                errors.into_boxed_slice(),
-            ));
-        }
+    fn collect_macro_items(&mut self, res: ExpandResult<Option<(Mark, Parse<ast::MacroItems>)>>) {
+        let Some((mark, _parse)) = res.value else { return };
 
         let tree_id = item_tree::TreeId::new(self.expander.current_file_id(), None);
         let item_tree = tree_id.item_tree(self.db);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
index a60b9f9f3ab..54cd57110ea 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
@@ -234,6 +234,14 @@ impl ItemScope {
         self.impls.iter().copied()
     }
 
+    pub fn all_macro_calls(&self) -> impl Iterator<Item = MacroCallId> + '_ {
+        self.macro_invocations.values().copied().chain(self.attr_macros.values().copied()).chain(
+            self.derive_macros.values().flat_map(|it| {
+                it.iter().flat_map(|it| it.derive_call_ids.iter().copied().flatten())
+            }),
+        )
+    }
+
     pub(crate) fn modules_in_scope(&self) -> impl Iterator<Item = (ModuleId, Visibility)> + '_ {
         self.types.values().copied().filter_map(|(def, vis, _)| match def {
             ModuleDefId::ModuleId(module) => Some((module, vis)),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index 30533cc45ab..262bc538b94 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -15,15 +15,13 @@ use hir_expand::{
     builtin_fn_macro::find_builtin_macro,
     name::{name, AsName, Name},
     proc_macro::CustomProcMacroExpander,
-    ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc,
-    MacroDefId, MacroDefKind,
+    ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
 };
 use itertools::{izip, Itertools};
 use la_arena::Idx;
 use limit::Limit;
 use rustc_hash::{FxHashMap, FxHashSet};
 use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContextId};
-use stdx::always;
 use syntax::ast;
 use triomphe::Arc;
 
@@ -1412,31 +1410,6 @@ impl DefCollector<'_> {
         }
         let file_id = macro_call_id.as_file();
 
-        // First, fetch the raw expansion result for purposes of error reporting. This goes through
-        // `parse_macro_expansion_error` to avoid depending on the full expansion result (to improve
-        // incrementality).
-        // FIXME: This kind of error fetching feels a bit odd?
-        let ExpandResult { value: errors, err } =
-            self.db.parse_macro_expansion_error(macro_call_id);
-        if let Some(err) = err {
-            let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
-            let diag = match err {
-                // why is this reported here?
-                hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
-                    always!(krate == loc.def.krate);
-                    DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate)
-                }
-                _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()),
-            };
-
-            self.def_map.diagnostics.push(diag);
-        }
-        if !errors.is_empty() {
-            let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
-            let diag = DefDiagnostic::macro_expansion_parse_error(module_id, loc.kind, errors);
-            self.def_map.diagnostics.push(diag);
-        }
-
         // Then, fetch and process the item tree. This will reuse the expansion result from above.
         let item_tree = self.db.file_item_tree(file_id);
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
index 8c7fdaaf58b..523a4c107b3 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
@@ -6,7 +6,7 @@ use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
 use hir_expand::{attrs::AttrId, ErasedAstId, MacroCallKind};
 use la_arena::Idx;
-use syntax::{ast, SyntaxError};
+use syntax::ast;
 
 use crate::{
     item_tree::{self, ItemTreeId},
@@ -23,8 +23,6 @@ pub enum DefDiagnosticKind {
     UnconfiguredCode { ast: ErasedAstId, cfg: CfgExpr, opts: CfgOptions },
     UnresolvedProcMacro { ast: MacroCallKind, krate: CrateId },
     UnresolvedMacroCall { ast: MacroCallKind, path: ModPath },
-    MacroError { ast: MacroCallKind, message: String },
-    MacroExpansionParseError { ast: MacroCallKind, errors: Box<[SyntaxError]> },
     UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
     InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize },
     MalformedDerive { ast: AstId<ast::Adt>, id: usize },
@@ -98,7 +96,7 @@ impl DefDiagnostic {
     // FIXME: This is used for a lot of things, unresolved proc macros, disabled proc macros, etc
     // yet the diagnostic handler in ide-diagnostics has to figure out what happened because this
     // struct loses all that information!
-    pub(crate) fn unresolved_proc_macro(
+    pub fn unresolved_proc_macro(
         container: LocalModuleId,
         ast: MacroCallKind,
         krate: CrateId,
@@ -106,25 +104,6 @@ impl DefDiagnostic {
         Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } }
     }
 
-    pub(crate) fn macro_error(
-        container: LocalModuleId,
-        ast: MacroCallKind,
-        message: String,
-    ) -> Self {
-        Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } }
-    }
-
-    pub(crate) fn macro_expansion_parse_error(
-        container: LocalModuleId,
-        ast: MacroCallKind,
-        errors: Box<[SyntaxError]>,
-    ) -> Self {
-        Self {
-            in_module: container,
-            kind: DefDiagnosticKind::MacroExpansionParseError { ast, errors },
-        }
-    }
-
     // FIXME: Whats the difference between this and unresolved_proc_macro
     pub(crate) fn unresolved_macro_call(
         container: LocalModuleId,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
index 2435a0c3103..2e5fa6131a7 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -132,7 +132,7 @@ pub trait ExpandDatabase: SourceDatabase {
     fn parse_macro_expansion_error(
         &self,
         macro_call: MacroCallId,
-    ) -> ExpandResult<Box<[SyntaxError]>>;
+    ) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>>;
 }
 
 /// This expands the given macro call, but with different arguments. This is
@@ -357,9 +357,14 @@ fn parse_macro_expansion(
 fn parse_macro_expansion_error(
     db: &dyn ExpandDatabase,
     macro_call_id: MacroCallId,
-) -> ExpandResult<Box<[SyntaxError]>> {
-    db.parse_macro_expansion(MacroFileId { macro_call_id })
-        .map(|it| it.0.errors().into_boxed_slice())
+) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>> {
+    let e: ExpandResult<Arc<[SyntaxError]>> =
+        db.parse_macro_expansion(MacroFileId { macro_call_id }).map(|it| Arc::from(it.0.errors()));
+    if e.value.is_empty() && e.err.is_none() {
+        None
+    } else {
+        Some(Arc::new(e))
+    }
 }
 
 pub(crate) fn parse_with_map(
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 338bd25ede3..4ab989bec2f 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -132,13 +132,13 @@ pub enum ExpandError {
     MacroDefinition,
     Mbe(mbe::ExpandError),
     RecursionOverflow,
-    Other(Box<Box<str>>),
-    ProcMacroPanic(Box<Box<str>>),
+    Other(Arc<Box<str>>),
+    ProcMacroPanic(Arc<Box<str>>),
 }
 
 impl ExpandError {
     pub fn other(msg: impl Into<Box<str>>) -> Self {
-        ExpandError::Other(Box::new(msg.into()))
+        ExpandError::Other(Arc::new(msg.into()))
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
index abed16fecde..def2578b0e3 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
@@ -8,6 +8,7 @@ use rustc_hash::FxHashMap;
 use span::Span;
 use stdx::never;
 use syntax::SmolStr;
+use triomphe::Arc;
 
 use crate::{db::ExpandDatabase, tt, ExpandError, ExpandResult};
 
@@ -157,7 +158,7 @@ impl CustomProcMacroExpander {
                         ProcMacroExpansionError::System(text)
                         | ProcMacroExpansionError::Panic(text) => ExpandResult::new(
                             tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
-                            ExpandError::ProcMacroPanic(Box::new(text.into_boxed_str())),
+                            ExpandError::ProcMacroPanic(Arc::new(text.into_boxed_str())),
                         ),
                     },
                 }
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index 6e3d366c88b..72272934ab7 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -13,6 +13,7 @@ pub use hir_def::VariantId;
 use hir_def::{body::SyntheticSyntax, hir::ExprOrPatId, path::ModPath, AssocItemId, DefWithBodyId};
 use hir_expand::{name::Name, HirFileId, InFile};
 use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange};
+use triomphe::Arc;
 
 use crate::{AssocItem, Field, Local, MacroKind, Trait, Type};
 
@@ -172,7 +173,7 @@ pub struct MacroError {
 pub struct MacroExpansionParseError {
     pub node: InFile<SyntaxNodePtr>,
     pub precise_location: Option<TextRange>,
-    pub errors: Box<[SyntaxError]>,
+    pub errors: Arc<[SyntaxError]>,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 275c1390f27..9fef3e0575d 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -59,7 +59,9 @@ use hir_def::{
     ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId, TypeOrConstParamId,
     TypeParamId, UnionId,
 };
-use hir_expand::{attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, MacroCallKind};
+use hir_expand::{
+    attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, AstId, MacroCallKind, ValueResult,
+};
 use hir_ty::{
     all_super_traits, autoderef, check_orphan_rules,
     consteval::{try_const_usize, unknown_const_as_generic, ConstExt},
@@ -79,7 +81,7 @@ use hir_ty::{
 use itertools::Itertools;
 use nameres::diagnostics::DefDiagnosticKind;
 use rustc_hash::FxHashSet;
-use span::Edition;
+use span::{Edition, MacroCallId};
 use stdx::{impl_from, never};
 use syntax::{
     ast::{self, HasAttrs as _, HasName},
@@ -559,6 +561,12 @@ impl Module {
             emit_def_diagnostic(db, acc, diag);
         }
 
+        if !self.id.is_block_module() {
+            // These are reported by the body of block modules
+            let scope = &def_map[self.id.local_id].scope;
+            scope.all_macro_calls().for_each(|it| macro_call_diagnostics(db, it, acc));
+        }
+
         for def in self.declarations(db) {
             match def {
                 ModuleDef::Module(m) => {
@@ -577,6 +585,10 @@ impl Module {
                         item.diagnostics(db, acc, style_lints);
                     }
 
+                    t.all_macro_calls(db)
+                        .iter()
+                        .for_each(|&(_ast, call_id)| macro_call_diagnostics(db, call_id, acc));
+
                     acc.extend(def.diagnostics(db, style_lints))
                 }
                 ModuleDef::Adt(adt) => {
@@ -621,6 +633,11 @@ impl Module {
                 // FIXME: Once we diagnose the inputs to builtin derives, we should at least extract those diagnostics somehow
                 continue;
             }
+            impl_def
+                .all_macro_calls(db)
+                .iter()
+                .for_each(|&(_ast, call_id)| macro_call_diagnostics(db, call_id, acc));
+
             let ast_id_map = db.ast_id_map(file_id);
 
             for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() {
@@ -809,6 +826,37 @@ impl Module {
     }
 }
 
+fn macro_call_diagnostics(
+    db: &dyn HirDatabase,
+    macro_call_id: MacroCallId,
+    acc: &mut Vec<AnyDiagnostic>,
+) {
+    let Some(e) = db.parse_macro_expansion_error(macro_call_id) else {
+        return;
+    };
+    let ValueResult { value: parse_errors, err } = &*e;
+    if let Some(err) = err {
+        let loc = db.lookup_intern_macro_call(macro_call_id);
+        let (node, precise_location, macro_name, kind) = precise_macro_call_location(&loc.kind, db);
+        let diag = match err {
+            &hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
+                UnresolvedProcMacro { node, precise_location, macro_name, kind, krate }.into()
+            }
+            err => MacroError { node, precise_location, message: err.to_string() }.into(),
+        };
+        acc.push(diag);
+    }
+
+    if !parse_errors.is_empty() {
+        let loc = db.lookup_intern_macro_call(macro_call_id);
+        let (node, precise_location, _, _) = precise_macro_call_location(&loc.kind, db);
+        acc.push(
+            MacroExpansionParseError { node, precise_location, errors: parse_errors.clone() }
+                .into(),
+        )
+    }
+}
+
 fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, m: Macro) {
     let id = db.macro_def(m.id);
     if let hir_expand::db::TokenExpander::DeclarativeMacro(expander) = db.macro_expander(id) {
@@ -888,16 +936,6 @@ fn emit_def_diagnostic_(
                 .into(),
             );
         }
-        DefDiagnosticKind::MacroError { ast, message } => {
-            let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
-            acc.push(MacroError { node, precise_location, message: message.clone() }.into());
-        }
-        DefDiagnosticKind::MacroExpansionParseError { ast, errors } => {
-            let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
-            acc.push(
-                MacroExpansionParseError { node, precise_location, errors: errors.clone() }.into(),
-            );
-        }
         DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => {
             let node = ast.to_node(db.upcast());
             // Must have a name, otherwise we wouldn't emit it.
@@ -1644,6 +1682,10 @@ impl DefWithBody {
             Module { id: def_map.module_id(DefMap::ROOT) }.diagnostics(db, acc, style_lints);
         }
 
+        source_map
+            .macro_calls()
+            .for_each(|(_ast_id, call_id)| macro_call_diagnostics(db, call_id.macro_call_id, acc));
+
         for diag in source_map.diagnostics() {
             acc.push(match diag {
                 BodyDiagnostic::InactiveCode { node, cfg, opts } => {
@@ -2445,6 +2487,14 @@ impl Trait {
             .filter(|(_, ty)| !count_required_only || !ty.has_default())
             .count()
     }
+
+    fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> {
+        db.trait_data(self.id)
+            .macro_calls
+            .as_ref()
+            .map(|it| it.as_ref().clone().into_boxed_slice())
+            .unwrap_or_default()
+    }
 }
 
 impl HasVisibility for Trait {
@@ -3765,6 +3815,14 @@ impl Impl {
     pub fn check_orphan_rules(self, db: &dyn HirDatabase) -> bool {
         check_orphan_rules(db, self.id)
     }
+
+    fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> {
+        db.impl_data(self.id)
+            .macro_calls
+            .as_ref()
+            .map(|it| it.as_ref().clone().into_boxed_slice())
+            .unwrap_or_default()
+    }
 }
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
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 dc96a1b03d0..057b03baef0 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -28,7 +28,7 @@ use hir_expand::{
     mod_path::path,
     name,
     name::{AsName, Name},
-    HirFileId, InFile, MacroFileId, MacroFileIdExt,
+    HirFileId, InFile, InMacroFile, MacroFileId, MacroFileIdExt,
 };
 use hir_ty::{
     diagnostics::{
@@ -118,7 +118,7 @@ impl SourceAnalyzer {
     fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprId> {
         let src = match expr {
             ast::Expr::MacroExpr(expr) => {
-                self.expand_expr(db, InFile::new(self.file_id, expr.macro_call()?))?
+                self.expand_expr(db, InFile::new(self.file_id, expr.macro_call()?))?.into()
             }
             _ => InFile::new(self.file_id, expr.clone()),
         };
@@ -145,20 +145,20 @@ impl SourceAnalyzer {
         &self,
         db: &dyn HirDatabase,
         expr: InFile<ast::MacroCall>,
-    ) -> Option<InFile<ast::Expr>> {
+    ) -> Option<InMacroFile<ast::Expr>> {
         let macro_file = self.body_source_map()?.node_macro_file(expr.as_ref())?;
-        let expanded = db.parse_or_expand(macro_file);
+        let expanded = db.parse_macro_expansion(macro_file).value.0.syntax_node();
         let res = if let Some(stmts) = ast::MacroStmts::cast(expanded.clone()) {
             match stmts.expr()? {
                 ast::Expr::MacroExpr(mac) => {
-                    self.expand_expr(db, InFile::new(macro_file, mac.macro_call()?))?
+                    self.expand_expr(db, InFile::new(macro_file.into(), mac.macro_call()?))?
                 }
-                expr => InFile::new(macro_file, expr),
+                expr => InMacroFile::new(macro_file, expr),
             }
         } else if let Some(call) = ast::MacroCall::cast(expanded.clone()) {
-            self.expand_expr(db, InFile::new(macro_file, call))?
+            self.expand_expr(db, InFile::new(macro_file.into(), call))?
         } else {
-            InFile::new(macro_file, ast::Expr::cast(expanded)?)
+            InMacroFile::new(macro_file, ast::Expr::cast(expanded)?)
         };
 
         Some(res)
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index 135824386af..15543a5d655 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -299,11 +299,10 @@ pub fn diagnostics(
 ) -> Vec<Diagnostic> {
     let _p = tracing::span!(tracing::Level::INFO, "diagnostics").entered();
     let sema = Semantics::new(db);
-    let parse = db.parse(file_id);
     let mut res = Vec::new();
 
     // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
-    res.extend(parse.errors().into_iter().take(128).map(|err| {
+    res.extend(db.parse_errors(file_id).as_deref().into_iter().flatten().take(128).map(|err| {
         Diagnostic::new(
             DiagnosticCode::RustcHardError("syntax-error"),
             format!("Syntax Error: {err}"),
@@ -342,7 +341,8 @@ pub fn diagnostics(
             AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_error(&ctx, &d),
             AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
             AnyDiagnostic::MacroExpansionParseError(d) => {
-                res.extend(d.errors.iter().take(32).map(|err| {
+                // FIXME: Point to the correct error span here, not just the macro-call name
+                res.extend(d.errors.iter().take(16).map(|err| {
                     {
                         Diagnostic::new(
                             DiagnosticCode::RustcHardError("syntax-error"),