about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/base-db/src/fixture.rs8
-rw-r--r--crates/base-db/src/input.rs30
-rw-r--r--crates/base-db/src/lib.rs2
-rw-r--r--crates/hir-def/src/nameres.rs7
-rw-r--r--crates/hir-def/src/nameres/collector.rs54
-rw-r--r--crates/hir-def/src/nameres/diagnostics.rs11
-rw-r--r--crates/hir-expand/src/proc_macro.rs10
-rw-r--r--crates/hir/src/diagnostics.rs3
-rw-r--r--crates/hir/src/lib.rs8
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs29
-rw-r--r--crates/ide/src/lib.rs2
-rw-r--r--crates/proc-macro-api/src/lib.rs16
-rw-r--r--crates/project-model/src/tests.rs110
-rw-r--r--crates/project-model/src/workspace.rs37
-rw-r--r--crates/rust-analyzer/src/reload.rs62
15 files changed, 245 insertions, 144 deletions
diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs
index 69585b44a1a..8e6e6a11abd 100644
--- a/crates/base-db/src/fixture.rs
+++ b/crates/base-db/src/fixture.rs
@@ -159,7 +159,7 @@ impl ChangeFixture {
                     meta.cfg.clone(),
                     meta.cfg,
                     meta.env,
-                    Default::default(),
+                    Ok(Vec::new()),
                     false,
                     origin,
                 );
@@ -194,7 +194,7 @@ impl ChangeFixture {
                 default_cfg.clone(),
                 default_cfg,
                 Env::default(),
-                Default::default(),
+                Ok(Vec::new()),
                 false,
                 CrateOrigin::CratesIo { repo: None },
             );
@@ -231,7 +231,7 @@ impl ChangeFixture {
                 CfgOptions::default(),
                 CfgOptions::default(),
                 Env::default(),
-                Vec::new(),
+                Ok(Vec::new()),
                 false,
                 CrateOrigin::Lang(LangCrateOrigin::Core),
             );
@@ -268,7 +268,7 @@ impl ChangeFixture {
                 CfgOptions::default(),
                 CfgOptions::default(),
                 Env::default(),
-                proc_macro,
+                Ok(proc_macro),
                 true,
                 CrateOrigin::CratesIo { repo: None },
             );
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index 9fcaa4b06dd..4d0a3a30123 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -231,6 +231,8 @@ pub enum ProcMacroExpansionError {
     System(String),
 }
 
+pub type ProcMacroLoadResult = Result<Vec<ProcMacro>, String>;
+
 #[derive(Debug, Clone)]
 pub struct ProcMacro {
     pub name: SmolStr,
@@ -254,7 +256,7 @@ pub struct CrateData {
     pub potential_cfg_options: CfgOptions,
     pub env: Env,
     pub dependencies: Vec<Dependency>,
-    pub proc_macro: Vec<ProcMacro>,
+    pub proc_macro: ProcMacroLoadResult,
     pub origin: CrateOrigin,
     pub is_proc_macro: bool,
 }
@@ -300,19 +302,19 @@ impl Dependency {
 impl CrateGraph {
     pub fn add_crate_root(
         &mut self,
-        file_id: FileId,
+        root_file_id: FileId,
         edition: Edition,
         display_name: Option<CrateDisplayName>,
         version: Option<String>,
         cfg_options: CfgOptions,
         potential_cfg_options: CfgOptions,
         env: Env,
-        proc_macro: Vec<ProcMacro>,
+        proc_macro: ProcMacroLoadResult,
         is_proc_macro: bool,
         origin: CrateOrigin,
     ) -> CrateId {
         let data = CrateData {
-            root_file_id: file_id,
+            root_file_id,
             edition,
             version,
             display_name,
@@ -628,7 +630,7 @@ mod tests {
             CfgOptions::default(),
             CfgOptions::default(),
             Env::default(),
-            Default::default(),
+            Ok(Vec::new()),
             false,
             CrateOrigin::CratesIo { repo: None },
         );
@@ -640,7 +642,7 @@ mod tests {
             CfgOptions::default(),
             CfgOptions::default(),
             Env::default(),
-            Default::default(),
+            Ok(Vec::new()),
             false,
             CrateOrigin::CratesIo { repo: None },
         );
@@ -652,7 +654,7 @@ mod tests {
             CfgOptions::default(),
             CfgOptions::default(),
             Env::default(),
-            Default::default(),
+            Ok(Vec::new()),
             false,
             CrateOrigin::CratesIo { repo: None },
         );
@@ -678,7 +680,7 @@ mod tests {
             CfgOptions::default(),
             CfgOptions::default(),
             Env::default(),
-            Default::default(),
+            Ok(Vec::new()),
             false,
             CrateOrigin::CratesIo { repo: None },
         );
@@ -690,7 +692,7 @@ mod tests {
             CfgOptions::default(),
             CfgOptions::default(),
             Env::default(),
-            Default::default(),
+            Ok(Vec::new()),
             false,
             CrateOrigin::CratesIo { repo: None },
         );
@@ -713,7 +715,7 @@ mod tests {
             CfgOptions::default(),
             CfgOptions::default(),
             Env::default(),
-            Default::default(),
+            Ok(Vec::new()),
             false,
             CrateOrigin::CratesIo { repo: None },
         );
@@ -725,7 +727,7 @@ mod tests {
             CfgOptions::default(),
             CfgOptions::default(),
             Env::default(),
-            Default::default(),
+            Ok(Vec::new()),
             false,
             CrateOrigin::CratesIo { repo: None },
         );
@@ -737,7 +739,7 @@ mod tests {
             CfgOptions::default(),
             CfgOptions::default(),
             Env::default(),
-            Default::default(),
+            Ok(Vec::new()),
             false,
             CrateOrigin::CratesIo { repo: None },
         );
@@ -760,7 +762,7 @@ mod tests {
             CfgOptions::default(),
             CfgOptions::default(),
             Env::default(),
-            Default::default(),
+            Ok(Vec::new()),
             false,
             CrateOrigin::CratesIo { repo: None },
         );
@@ -772,7 +774,7 @@ mod tests {
             CfgOptions::default(),
             CfgOptions::default(),
             Env::default(),
-            Default::default(),
+            Ok(Vec::new()),
             false,
             CrateOrigin::CratesIo { repo: None },
         );
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index 2454698bb61..f5622ecf607 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -13,7 +13,7 @@ pub use crate::{
     input::{
         CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency,
         Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
-        ProcMacroId, ProcMacroKind, SourceRoot, SourceRootId,
+        ProcMacroId, ProcMacroKind, ProcMacroLoadResult, SourceRoot, SourceRootId,
     },
 };
 pub use salsa::{self, Cancelled};
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index e923d883ecf..536eecf0206 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -103,6 +103,8 @@ pub struct DefMap {
     /// Side table for resolving derive helpers.
     exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
     fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>,
+    /// The error that occurred when failing to load the proc-macro dll.
+    proc_macro_loading_error: Option<Box<str>>,
 
     /// Custom attributes registered with `#![register_attr]`.
     registered_attrs: Vec<SmolStr>,
@@ -273,6 +275,7 @@ impl DefMap {
             extern_prelude: FxHashMap::default(),
             exported_derives: FxHashMap::default(),
             fn_proc_macro_mapping: FxHashMap::default(),
+            proc_macro_loading_error: None,
             prelude: None,
             root,
             modules,
@@ -305,6 +308,9 @@ impl DefMap {
     pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option<ProcMacroId> {
         self.fn_proc_macro_mapping.get(&id).copied()
     }
+    pub fn proc_macro_loading_error(&self) -> Option<&str> {
+        self.proc_macro_loading_error.as_deref()
+    }
 
     pub(crate) fn krate(&self) -> CrateId {
         self.krate
@@ -460,6 +466,7 @@ impl DefMap {
             registered_attrs,
             registered_tools,
             fn_proc_macro_mapping,
+            proc_macro_loading_error: _,
             block: _,
             edition: _,
             recursion_limit: _,
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 7fea46bee3c..6a9f569ea81 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -74,19 +74,26 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
     }
 
     let cfg_options = &krate.cfg_options;
-    let proc_macros = krate
-        .proc_macro
-        .iter()
-        .enumerate()
-        .map(|(idx, it)| {
-            // FIXME: a hacky way to create a Name from string.
-            let name = tt::Ident { text: it.name.clone(), id: tt::TokenId::unspecified() };
-            (
-                name.as_name(),
-                ProcMacroExpander::new(def_map.krate, base_db::ProcMacroId(idx as u32)),
-            )
-        })
-        .collect();
+    let proc_macros = match &krate.proc_macro {
+        Ok(proc_macros) => {
+            proc_macros
+                .iter()
+                .enumerate()
+                .map(|(idx, it)| {
+                    // FIXME: a hacky way to create a Name from string.
+                    let name = tt::Ident { text: it.name.clone(), id: tt::TokenId::unspecified() };
+                    (
+                        name.as_name(),
+                        ProcMacroExpander::new(def_map.krate, base_db::ProcMacroId(idx as u32)),
+                    )
+                })
+                .collect()
+        }
+        Err(e) => {
+            def_map.proc_macro_loading_error = Some(e.clone().into_boxed_str());
+            Vec::new()
+        }
+    };
     let is_proc_macro = krate.is_proc_macro;
 
     let mut collector = DefCollector {
@@ -1128,7 +1135,19 @@ impl DefCollector<'_> {
 
                     let def = match resolver(path.clone()) {
                         Some(def) if def.is_attribute() => def,
-                        _ => return true,
+                        _ => {
+                            self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
+                                directive.module_id,
+                                MacroCallKind::Attr {
+                                    ast_id,
+                                    attr_args: Default::default(),
+                                    invoc_attr_index: attr.id.ast_index,
+                                    is_derive: false,
+                                },
+                                None,
+                            ));
+                            return true;
+                        }
                     };
                     if matches!(
                         def,
@@ -1232,6 +1251,7 @@ impl DefCollector<'_> {
                             self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
                                 directive.module_id,
                                 loc.kind,
+                                Some(loc.def.krate),
                             ));
 
                             return recollect_without(self);
@@ -1283,7 +1303,11 @@ impl DefCollector<'_> {
             let diag = match err {
                 hir_expand::ExpandError::UnresolvedProcMacro => {
                     // Missing proc macros are non-fatal, so they are handled specially.
-                    DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone())
+                    DefDiagnostic::unresolved_proc_macro(
+                        module_id,
+                        loc.kind.clone(),
+                        Some(loc.def.krate),
+                    )
                 }
                 _ => DefDiagnostic::macro_error(module_id, loc.kind.clone(), err.to_string()),
             };
diff --git a/crates/hir-def/src/nameres/diagnostics.rs b/crates/hir-def/src/nameres/diagnostics.rs
index dd3ff92cb3b..3ebc5629d7c 100644
--- a/crates/hir-def/src/nameres/diagnostics.rs
+++ b/crates/hir-def/src/nameres/diagnostics.rs
@@ -1,5 +1,6 @@
 //! Diagnostics emitted during DefMap construction.
 
+use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
 use hir_expand::MacroCallKind;
 use la_arena::Idx;
@@ -23,7 +24,7 @@ pub enum DefDiagnosticKind {
 
     UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
 
-    UnresolvedProcMacro { ast: MacroCallKind },
+    UnresolvedProcMacro { ast: MacroCallKind, krate: Option<CrateId> },
 
     UnresolvedMacroCall { ast: MacroCallKind, path: ModPath },
 
@@ -81,8 +82,12 @@ impl DefDiagnostic {
         Self { in_module: container, kind: DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
     }
 
-    pub(super) fn unresolved_proc_macro(container: LocalModuleId, ast: MacroCallKind) -> Self {
-        Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast } }
+    pub(super) fn unresolved_proc_macro(
+        container: LocalModuleId,
+        ast: MacroCallKind,
+        krate: Option<CrateId>,
+    ) -> Self {
+        Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } }
     }
 
     pub(super) fn macro_error(
diff --git a/crates/hir-expand/src/proc_macro.rs b/crates/hir-expand/src/proc_macro.rs
index df6c38761c3..b2686592a55 100644
--- a/crates/hir-expand/src/proc_macro.rs
+++ b/crates/hir-expand/src/proc_macro.rs
@@ -34,7 +34,15 @@ impl ProcMacroExpander {
         match self.proc_macro_id {
             Some(id) => {
                 let krate_graph = db.crate_graph();
-                let proc_macro = match krate_graph[self.krate].proc_macro.get(id.0 as usize) {
+                let proc_macros = match &krate_graph[self.krate].proc_macro {
+                    Ok(proc_macros) => proc_macros,
+                    Err(e) => {
+                        return ExpandResult::only_err(ExpandError::Other(
+                            e.clone().into_boxed_str(),
+                        ))
+                    }
+                };
+                let proc_macro = match proc_macros.get(id.0 as usize) {
                     Some(proc_macro) => proc_macro,
                     None => {
                         return ExpandResult::only_err(ExpandError::Other(
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 0c88d15b089..1f65c05c1ea 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -3,6 +3,7 @@
 //!
 //! This probably isn't the best way to do this -- ideally, diagnistics should
 //! be expressed in terms of hir types themselves.
+use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
 use hir_def::path::ModPath;
@@ -87,6 +88,8 @@ pub struct UnresolvedProcMacro {
     pub precise_location: Option<TextRange>,
     pub macro_name: Option<String>,
     pub kind: MacroKind,
+    /// The crate id of the proc-macro this macro belongs to, or `None` if the proc-macro can't be found.
+    pub krate: Option<CrateId>,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 4620d0c03ac..3f0d586bf64 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -627,7 +627,7 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag:
             );
         }
 
-        DefDiagnosticKind::UnresolvedProcMacro { ast } => {
+        DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => {
             let (node, precise_location, macro_name, kind) = match ast {
                 MacroCallKind::FnLike { ast_id, .. } => {
                     let node = ast_id.to_node(db.upcast());
@@ -689,7 +689,10 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag:
                     )
                 }
             };
-            acc.push(UnresolvedProcMacro { node, precise_location, macro_name, kind }.into());
+            acc.push(
+                UnresolvedProcMacro { node, precise_location, macro_name, kind, krate: *krate }
+                    .into(),
+            );
         }
 
         DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
@@ -1163,6 +1166,7 @@ impl DefWithBody {
                         precise_location: None,
                         macro_name: None,
                         kind: MacroKind::ProcMacro,
+                        krate: None,
                     }
                     .into(),
                 ),
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs b/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
index 37350a7aaf1..5ebfe33dab1 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
@@ -1,3 +1,5 @@
+use hir::db::DefDatabase;
+
 use crate::{Diagnostic, DiagnosticsContext, Severity};
 
 // Diagnostic: unresolved-proc-macro
@@ -29,19 +31,24 @@ pub(crate) fn unresolved_proc_macro(
         Some(name) => format!("proc macro `{}` not expanded", name),
         None => "proc macro not expanded".to_string(),
     };
-    let (message, severity) = if config_enabled {
-        (message, Severity::Error)
-    } else {
-        let message = match d.kind {
-            hir::MacroKind::Attr if proc_macros_enabled => {
-                format!("{message}{}", " (attribute macro expansion is disabled)")
+    let severity = if config_enabled { Severity::Error } else { Severity::WeakWarning };
+    let def_map = d.krate.map(|krate| ctx.sema.db.crate_def_map(krate));
+    let message = format!(
+        "{message}: {}",
+        if config_enabled {
+            match def_map.as_ref().and_then(|def_map| def_map.proc_macro_loading_error()) {
+                Some(e) => e,
+                None => "proc macro not found",
             }
-            _ => {
-                format!("{message}{}", " (proc-macro expansion is disabled)")
+        } else {
+            match d.kind {
+                hir::MacroKind::Attr if proc_macros_enabled => {
+                    "attribute macro expansion is disabled"
+                }
+                _ => "proc-macro expansion is disabled",
             }
-        };
-        (message, Severity::WeakWarning)
-    };
+        },
+    );
 
     Diagnostic::new("unresolved-proc-macro", message, display_range).severity(severity)
 }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index aab5ceda366..07a7fbd7837 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -233,7 +233,7 @@ impl Analysis {
             cfg_options.clone(),
             cfg_options,
             Env::default(),
-            Default::default(),
+            Ok(Vec::new()),
             false,
             CrateOrigin::CratesIo { repo: None },
         );
diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs
index eb7a28b972b..4a30168ca51 100644
--- a/crates/proc-macro-api/src/lib.rs
+++ b/crates/proc-macro-api/src/lib.rs
@@ -118,16 +118,13 @@ impl ProcMacroServer {
         Ok(ProcMacroServer { process: Arc::new(Mutex::new(process)) })
     }
 
-    pub fn load_dylib(
-        &self,
-        dylib: MacroDylib,
-    ) -> Result<Result<Vec<ProcMacro>, String>, ServerError> {
+    pub fn load_dylib(&self, dylib: MacroDylib) -> Result<Vec<ProcMacro>, ServerError> {
         let _p = profile::span("ProcMacroClient::by_dylib_path");
         let macros =
             self.process.lock().unwrap_or_else(|e| e.into_inner()).find_proc_macros(&dylib.path)?;
 
-        let res = macros.map(|macros| {
-            macros
+        match macros {
+            Ok(macros) => Ok(macros
                 .into_iter()
                 .map(|(name, kind)| ProcMacro {
                     process: self.process.clone(),
@@ -135,10 +132,9 @@ impl ProcMacroServer {
                     kind,
                     dylib_path: dylib.path.clone(),
                 })
-                .collect()
-        });
-
-        Ok(res)
+                .collect()),
+            Err(message) => Err(ServerError { message, io: None }),
+        }
     }
 }
 
diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs
index e72903e55fe..1a43935470b 100644
--- a/crates/project-model/src/tests.rs
+++ b/crates/project-model/src/tests.rs
@@ -88,7 +88,7 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
 }
 
 fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph {
-    project_workspace.to_crate_graph(&mut |_, _| Vec::new(), &mut {
+    project_workspace.to_crate_graph(&mut |_, _| Ok(Vec::new()), &mut {
         let mut counter = 0;
         move |_path| {
             counter += 1;
@@ -172,7 +172,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: None,
                         },
@@ -245,7 +247,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: None,
                         },
@@ -308,7 +312,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
                             },
                         },
                         dependencies: [],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: Some(
                                 "https://github.com/rust-lang/libc",
@@ -383,7 +389,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: None,
                         },
@@ -456,7 +464,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: None,
                         },
@@ -544,7 +554,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: None,
                         },
@@ -619,7 +631,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: None,
                         },
@@ -682,7 +696,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
                             },
                         },
                         dependencies: [],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: Some(
                                 "https://github.com/rust-lang/libc",
@@ -759,7 +775,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: None,
                         },
@@ -834,7 +852,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: None,
                         },
@@ -913,7 +933,9 @@ fn cargo_hello_world_project_model() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: None,
                         },
@@ -988,7 +1010,9 @@ fn cargo_hello_world_project_model() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: None,
                         },
@@ -1053,7 +1077,9 @@ fn cargo_hello_world_project_model() {
                             },
                         },
                         dependencies: [],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: Some(
                                 "https://github.com/rust-lang/libc",
@@ -1130,7 +1156,9 @@ fn cargo_hello_world_project_model() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: None,
                         },
@@ -1205,7 +1233,9 @@ fn cargo_hello_world_project_model() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: None,
                         },
@@ -1260,7 +1290,9 @@ fn rust_project_hello_world_project_model() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: Lang(
                             Alloc,
                         ),
@@ -1292,7 +1324,9 @@ fn rust_project_hello_world_project_model() {
                             entries: {},
                         },
                         dependencies: [],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: Lang(
                             Other,
                         ),
@@ -1324,7 +1358,9 @@ fn rust_project_hello_world_project_model() {
                             entries: {},
                         },
                         dependencies: [],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: Lang(
                             Other,
                         ),
@@ -1366,7 +1402,9 @@ fn rust_project_hello_world_project_model() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: Lang(
                             Other,
                         ),
@@ -1398,7 +1436,9 @@ fn rust_project_hello_world_project_model() {
                             entries: {},
                         },
                         dependencies: [],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: Lang(
                             Core,
                         ),
@@ -1467,7 +1507,9 @@ fn rust_project_hello_world_project_model() {
                                 prelude: false,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: CratesIo {
                             repo: None,
                         },
@@ -1499,7 +1541,9 @@ fn rust_project_hello_world_project_model() {
                             entries: {},
                         },
                         dependencies: [],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: Lang(
                             Other,
                         ),
@@ -1531,7 +1575,9 @@ fn rust_project_hello_world_project_model() {
                             entries: {},
                         },
                         dependencies: [],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: Lang(
                             Other,
                         ),
@@ -1563,7 +1609,9 @@ fn rust_project_hello_world_project_model() {
                             entries: {},
                         },
                         dependencies: [],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: Lang(
                             Other,
                         ),
@@ -1595,7 +1643,9 @@ fn rust_project_hello_world_project_model() {
                             entries: {},
                         },
                         dependencies: [],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: Lang(
                             Test,
                         ),
@@ -1709,7 +1759,9 @@ fn rust_project_hello_world_project_model() {
                                 prelude: true,
                             },
                         ],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: Lang(
                             Std,
                         ),
@@ -1741,7 +1793,9 @@ fn rust_project_hello_world_project_model() {
                             entries: {},
                         },
                         dependencies: [],
-                        proc_macro: [],
+                        proc_macro: Ok(
+                            [],
+                        ),
                         origin: Lang(
                             Other,
                         ),
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index a94a38a17da..8982a9904ec 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -7,7 +7,7 @@ use std::{collections::VecDeque, fmt, fs, process::Command};
 use anyhow::{format_err, Context, Result};
 use base_db::{
     CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env,
-    FileId, LangCrateOrigin, ProcMacro,
+    FileId, LangCrateOrigin, ProcMacroLoadResult,
 };
 use cfg::{CfgDiff, CfgOptions};
 use paths::{AbsPath, AbsPathBuf};
@@ -389,7 +389,7 @@ impl ProjectWorkspace {
 
     pub fn to_crate_graph(
         &self,
-        load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> Vec<ProcMacro>,
+        load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
         load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
     ) -> CrateGraph {
         let _p = profile::span("ProjectWorkspace::to_crate_graph");
@@ -434,7 +434,7 @@ impl ProjectWorkspace {
 
 fn project_json_to_crate_graph(
     rustc_cfg: Vec<CfgFlag>,
-    load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> Vec<ProcMacro>,
+    load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
     project: &ProjectJson,
     sysroot: &Option<Sysroot>,
@@ -454,12 +454,13 @@ fn project_json_to_crate_graph(
         })
         .map(|(crate_id, krate, file_id)| {
             let env = krate.env.clone().into_iter().collect();
-            let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| {
-                load_proc_macro(
+            let proc_macro = match krate.proc_macro_dylib_path.clone() {
+                Some(it) => load_proc_macro(
                     krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""),
                     &it,
-                )
-            });
+                ),
+                None => Ok(Vec::new()),
+            };
 
             let target_cfgs = match krate.target.as_deref() {
                 Some(target) => {
@@ -480,7 +481,7 @@ fn project_json_to_crate_graph(
                     cfg_options.clone(),
                     cfg_options,
                     env,
-                    proc_macro.unwrap_or_default(),
+                    proc_macro,
                     krate.is_proc_macro,
                     if krate.display_name.is_some() {
                         CrateOrigin::CratesIo { repo: krate.repository.clone() }
@@ -521,7 +522,7 @@ fn project_json_to_crate_graph(
 fn cargo_to_crate_graph(
     rustc_cfg: Vec<CfgFlag>,
     override_cfg: &CfgOverrides,
-    load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> Vec<ProcMacro>,
+    load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
     cargo: &CargoWorkspace,
     build_scripts: &WorkspaceBuildScripts,
@@ -708,7 +709,7 @@ fn detached_files_to_crate_graph(
             cfg_options.clone(),
             cfg_options.clone(),
             Env::default(),
-            Vec::new(),
+            Ok(Vec::new()),
             false,
             CrateOrigin::CratesIo { repo: None },
         );
@@ -724,7 +725,7 @@ fn handle_rustc_crates(
     crate_graph: &mut CrateGraph,
     cfg_options: &CfgOptions,
     override_cfg: &CfgOverrides,
-    load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> Vec<ProcMacro>,
+    load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
     pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
     public_deps: &SysrootPublicDeps,
     cargo: &CargoWorkspace,
@@ -840,7 +841,7 @@ fn add_target_crate_root(
     pkg: &PackageData,
     build_data: Option<&BuildScriptOutput>,
     cfg_options: &CfgOptions,
-    load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
+    load_proc_macro: &mut dyn FnMut(&AbsPath) -> ProcMacroLoadResult,
     file_id: FileId,
     cargo_name: &str,
     is_proc_macro: bool,
@@ -866,11 +867,10 @@ fn add_target_crate_root(
         }
     }
 
-    let proc_macro = build_data
-        .as_ref()
-        .and_then(|it| it.proc_macro_dylib_path.as_ref())
-        .map(|it| load_proc_macro(it))
-        .unwrap_or_default();
+    let proc_macro = match build_data.as_ref().and_then(|it| it.proc_macro_dylib_path.as_ref()) {
+        Some(it) => load_proc_macro(it),
+        None => Ok(Vec::new()),
+    };
 
     let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
     let mut potential_cfg_options = cfg_options.clone();
@@ -922,7 +922,6 @@ fn sysroot_to_crate_graph(
             let file_id = load(&sysroot[krate].root)?;
 
             let env = Env::default();
-            let proc_macro = vec![];
             let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
             let crate_id = crate_graph.add_crate_root(
                 file_id,
@@ -932,7 +931,7 @@ fn sysroot_to_crate_graph(
                 cfg_options.clone(),
                 cfg_options.clone(),
                 env,
-                proc_macro,
+                Ok(Vec::new()),
                 false,
                 CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
             );
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 62a446ce2a6..3f129efd95b 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -19,7 +19,7 @@ use hir::db::DefDatabase;
 use ide::Change;
 use ide_db::base_db::{
     CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind,
-    SourceRoot, VfsPath,
+    ProcMacroLoadResult, SourceRoot, VfsPath,
 };
 use proc_macro_api::{MacroDylib, ProcMacroServer};
 use project_model::{ProjectWorkspace, WorkspaceBuildScripts};
@@ -536,45 +536,37 @@ impl SourceRootConfig {
 /// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
 /// with an identity dummy expander.
 pub(crate) fn load_proc_macro(
-    client: Option<&ProcMacroServer>,
+    server: Option<&ProcMacroServer>,
     path: &AbsPath,
     dummy_replace: &[Box<str>],
-) -> Vec<ProcMacro> {
-    let dylib = match MacroDylib::new(path.to_path_buf()) {
-        Ok(it) => it,
-        Err(err) => {
-            // FIXME: that's not really right -- we store this error in a
-            // persistent status.
-            tracing::warn!("failed to load proc macro: {}", err);
-            return Vec::new();
+) -> ProcMacroLoadResult {
+    let res: Result<_, String> = (|| {
+        let dylib = MacroDylib::new(path.to_path_buf())
+            .map_err(|io| format!("Proc-macro dylib loading failed: {io}"))?;
+        Ok(if let Some(it) = server {
+            let vec = it.load_dylib(dylib).map_err(|e| format!("{e}"))?;
+            vec.into_iter()
+                .map(|expander| expander_to_proc_macro(expander, dummy_replace))
+                .collect()
+        } else {
+            Vec::new()
+        })
+    })();
+    return match res {
+        Ok(proc_macros) => {
+            tracing::info!(
+                "Loaded proc-macros for {}: {:?}",
+                path.display(),
+                proc_macros.iter().map(|it| it.name.clone()).collect::<Vec<_>>()
+            );
+            Ok(proc_macros)
+        }
+        Err(e) => {
+            tracing::warn!("proc-macro loading for {} failed: {e}", path.display());
+            Err(e)
         }
     };
 
-    let proc_macros: Vec<_> = client
-        .map(|it| it.load_dylib(dylib))
-        .into_iter()
-        .flat_map(|it| match it {
-            Ok(Ok(macros)) => macros,
-            Err(err) => {
-                tracing::error!("proc macro server crashed: {}", err);
-                Vec::new()
-            }
-            Ok(Err(err)) => {
-                // FIXME: that's not really right -- we store this error in a
-                // persistent status.
-                tracing::warn!("failed to load proc macro: {}", err);
-                Vec::new()
-            }
-        })
-        .map(|expander| expander_to_proc_macro(expander, dummy_replace))
-        .collect();
-    tracing::info!(
-        "Loaded proc-macros for {}: {:?}",
-        path.display(),
-        proc_macros.iter().map(|it| it.name.clone()).collect::<Vec<_>>()
-    );
-    return proc_macros;
-
     fn expander_to_proc_macro(
         expander: proc_macro_api::ProcMacro,
         dummy_replace: &[Box<str>],