Restructure proc-macro loading erros, differentiate hard error property on kind

This commit is contained in:
Lukas Wirth 2025-07-03 09:28:53 +02:00
parent 7c3de9df36
commit e54759083a
11 changed files with 105 additions and 63 deletions

View file

@ -6,6 +6,7 @@
//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
//! actual IO is done and lowered to input.
use std::error::Error;
use std::hash::BuildHasherDefault;
use std::{fmt, mem, ops};
@ -22,7 +23,49 @@ use vfs::{AbsPathBuf, AnchoredPath, FileId, VfsPath, file_set::FileSet};
use crate::{CrateWorkspaceData, EditionedFileId, FxIndexSet, RootQueryDb};
pub type ProcMacroPaths = FxHashMap<CrateBuilderId, Result<(String, AbsPathBuf), String>>;
pub type ProcMacroPaths =
FxHashMap<CrateBuilderId, Result<(String, AbsPathBuf), ProcMacroLoadingError>>;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ProcMacroLoadingError {
Disabled,
FailedToBuild,
MissingDylibPath,
NotYetBuilt,
NoProcMacros,
ProcMacroSrvError(Box<str>),
}
impl ProcMacroLoadingError {
pub fn is_hard_error(&self) -> bool {
match self {
ProcMacroLoadingError::Disabled | ProcMacroLoadingError::NotYetBuilt => false,
ProcMacroLoadingError::FailedToBuild
| ProcMacroLoadingError::MissingDylibPath
| ProcMacroLoadingError::NoProcMacros
| ProcMacroLoadingError::ProcMacroSrvError(_) => true,
}
}
}
impl Error for ProcMacroLoadingError {}
impl fmt::Display for ProcMacroLoadingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ProcMacroLoadingError::Disabled => write!(f, "proc-macro expansion is disabled"),
ProcMacroLoadingError::FailedToBuild => write!(f, "proc-macro failed to build"),
ProcMacroLoadingError::MissingDylibPath => {
write!(f, "proc-macro crate build data is missing a dylib path")
}
ProcMacroLoadingError::NotYetBuilt => write!(f, "proc-macro not yet built"),
ProcMacroLoadingError::NoProcMacros => {
write!(f, "proc macro library has no proc macros")
}
ProcMacroLoadingError::ProcMacroSrvError(msg) => {
write!(f, "proc macro server error: {msg}")
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SourceRootId(pub u32);

View file

@ -14,8 +14,9 @@ pub use crate::{
input::{
BuiltCrateData, BuiltDependency, Crate, CrateBuilder, CrateBuilderId, CrateDataBuilder,
CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CratesIdMap, CratesMap,
DependencyBuilder, Env, ExtraCrateData, LangCrateOrigin, ProcMacroPaths, ReleaseChannel,
SourceRoot, SourceRootId, TargetLayoutLoadResult, UniqueCrateData,
DependencyBuilder, Env, ExtraCrateData, LangCrateOrigin, ProcMacroLoadingError,
ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult,
UniqueCrateData,
},
};
use dashmap::{DashMap, mapref::entry::Entry};

View file

@ -199,9 +199,9 @@ impl ExpandErrorKind {
},
&ExpandErrorKind::MissingProcMacroExpander(def_crate) => {
match db.proc_macros_for_crate(def_crate).as_ref().and_then(|it| it.get_error()) {
Some((e, hard_err)) => RenderedExpandError {
message: e.to_owned(),
error: hard_err,
Some(e) => RenderedExpandError {
message: e.to_string(),
error: e.is_hard_error(),
kind: RenderedExpandError::GENERAL_KIND,
},
None => RenderedExpandError {

View file

@ -46,7 +46,7 @@ pub fn prettify_macro_expansion(
} else if let Some(crate_name) = &macro_def_crate.extra_data(db).display_name {
make::tokens::ident(crate_name.crate_name().as_str())
} else {
return dollar_crate.clone();
dollar_crate.clone()
}
});
if replacement.text() == "$crate" {

View file

@ -4,7 +4,7 @@ use core::fmt;
use std::any::Any;
use std::{panic::RefUnwindSafe, sync};
use base_db::{Crate, CrateBuilderId, CratesIdMap, Env};
use base_db::{Crate, CrateBuilderId, CratesIdMap, Env, ProcMacroLoadingError};
use intern::Symbol;
use rustc_hash::FxHashMap;
use span::Span;
@ -53,8 +53,8 @@ pub enum ProcMacroExpansionError {
System(String),
}
pub type ProcMacroLoadResult = Result<Vec<ProcMacro>, (String, bool)>;
type StoredProcMacroLoadResult = Result<Box<[ProcMacro]>, (Box<str>, bool)>;
pub type ProcMacroLoadResult = Result<Vec<ProcMacro>, ProcMacroLoadingError>;
type StoredProcMacroLoadResult = Result<Box<[ProcMacro]>, ProcMacroLoadingError>;
#[derive(Default, Debug)]
pub struct ProcMacrosBuilder(FxHashMap<CrateBuilderId, Arc<CrateProcMacros>>);
@ -77,9 +77,7 @@ impl ProcMacrosBuilder {
proc_macros_crate,
match proc_macro {
Ok(it) => Arc::new(CrateProcMacros(Ok(it.into_boxed_slice()))),
Err((e, hard_err)) => {
Arc::new(CrateProcMacros(Err((e.into_boxed_str(), hard_err))))
}
Err(e) => Arc::new(CrateProcMacros(Err(e))),
},
);
}
@ -139,8 +137,8 @@ impl CrateProcMacros {
)
}
pub fn get_error(&self) -> Option<(&str, bool)> {
self.0.as_ref().err().map(|(e, hard_err)| (&**e, *hard_err))
pub fn get_error(&self) -> Option<&ProcMacroLoadingError> {
self.0.as_ref().err()
}
/// Fetch the [`CustomProcMacroExpander`]s and their corresponding names for the given crate.

View file

@ -36,16 +36,16 @@ pub use hir_ty::{
};
macro_rules! diagnostics {
($($diag:ident $(<$lt:lifetime>)?,)*) => {
($AnyDiagnostic:ident <$db:lifetime> -> $($diag:ident $(<$lt:lifetime>)?,)*) => {
#[derive(Debug)]
pub enum AnyDiagnostic<'db> {$(
pub enum $AnyDiagnostic<$db> {$(
$diag(Box<$diag $(<$lt>)?>),
)*}
$(
impl<'db> From<$diag $(<$lt>)?> for AnyDiagnostic<'db> {
fn from(d: $diag $(<$lt>)?) -> AnyDiagnostic<'db> {
AnyDiagnostic::$diag(Box::new(d))
impl<$db> From<$diag $(<$lt>)?> for $AnyDiagnostic<$db> {
fn from(d: $diag $(<$lt>)?) -> $AnyDiagnostic<$db> {
$AnyDiagnostic::$diag(Box::new(d))
}
}
)*
@ -66,7 +66,7 @@ macro_rules! diagnostics {
// }, ...
// ]
diagnostics![
diagnostics![AnyDiagnostic<'db> ->
AwaitOutsideOfAsync,
BreakOutsideOfLoop,
CastToUnsized<'db>,

View file

@ -175,7 +175,7 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
// ast::TuplePat(it) => (),
// FIXME
// ast::SlicePat(it) => (),
_ => return None,
_ => None,
}
}
}

View file

@ -912,7 +912,7 @@ fn handle_as_ref_str(
) -> Option<(ReferenceConversionType, bool)> {
let str_type = hir::BuiltinType::str().ty(db);
ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[str_type.clone()])
ty.impls_trait(db, famous_defs.core_convert_AsRef()?, std::slice::from_ref(&str_type))
.then_some((ReferenceConversionType::AsRefStr, could_deref_to_target(ty, &str_type, db)))
}
@ -924,10 +924,11 @@ fn handle_as_ref_slice(
let type_argument = ty.type_arguments().next()?;
let slice_type = hir::Type::new_slice(type_argument);
ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[slice_type.clone()]).then_some((
ReferenceConversionType::AsRefSlice,
could_deref_to_target(ty, &slice_type, db),
))
ty.impls_trait(db, famous_defs.core_convert_AsRef()?, std::slice::from_ref(&slice_type))
.then_some((
ReferenceConversionType::AsRefSlice,
could_deref_to_target(ty, &slice_type, db),
))
}
fn handle_dereferenced(
@ -937,10 +938,11 @@ fn handle_dereferenced(
) -> Option<(ReferenceConversionType, bool)> {
let type_argument = ty.type_arguments().next()?;
ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[type_argument.clone()]).then_some((
ReferenceConversionType::Dereferenced,
could_deref_to_target(ty, &type_argument, db),
))
ty.impls_trait(db, famous_defs.core_convert_AsRef()?, std::slice::from_ref(&type_argument))
.then_some((
ReferenceConversionType::Dereferenced,
could_deref_to_target(ty, &type_argument, db),
))
}
fn handle_option_as_ref(

View file

@ -11,7 +11,7 @@ use hir_expand::proc_macro::{
};
use ide_db::{
ChangeWithProcMacros, FxHashMap, RootDatabase,
base_db::{CrateGraphBuilder, Env, SourceRoot, SourceRootId},
base_db::{CrateGraphBuilder, Env, ProcMacroLoadingError, SourceRoot, SourceRootId},
prime_caches,
};
use itertools::Itertools;
@ -81,19 +81,16 @@ pub fn load_workspace(
ProcMacroServerChoice::Sysroot => ws
.find_sysroot_proc_macro_srv()
.and_then(|it| ProcMacroClient::spawn(&it, extra_env).map_err(Into::into))
.map_err(|e| (e, true)),
ProcMacroServerChoice::Explicit(path) => {
ProcMacroClient::spawn(path, extra_env).map_err(Into::into).map_err(|e| (e, true))
}
ProcMacroServerChoice::None => {
Err((anyhow::format_err!("proc macro server disabled"), false))
}
.map_err(|e| ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str())),
ProcMacroServerChoice::Explicit(path) => ProcMacroClient::spawn(path, extra_env)
.map_err(|e| ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str())),
ProcMacroServerChoice::None => Err(ProcMacroLoadingError::Disabled),
};
match &proc_macro_server {
Ok(server) => {
tracing::info!(path=%server.server_path(), "Proc-macro server started")
}
Err((e, _)) => {
Err(e) => {
tracing::info!(%e, "Failed to start proc-macro server")
}
}
@ -112,21 +109,18 @@ pub fn load_workspace(
let proc_macros = {
let proc_macro_server = match &proc_macro_server {
Ok(it) => Ok(it),
Err((e, hard_err)) => Err((e.to_string(), *hard_err)),
Err(e) => Err(ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str())),
};
proc_macros
.into_iter()
.map(|(crate_id, path)| {
(
crate_id,
path.map_or_else(
|e| Err((e, true)),
|(_, path)| {
proc_macro_server.as_ref().map_err(Clone::clone).and_then(
|proc_macro_server| load_proc_macro(proc_macro_server, &path, &[]),
)
},
),
path.map_or_else(Err, |(_, path)| {
proc_macro_server.as_ref().map_err(Clone::clone).and_then(
|proc_macro_server| load_proc_macro(proc_macro_server, &path, &[]),
)
}),
)
})
.collect()
@ -391,11 +385,13 @@ pub fn load_proc_macro(
path: &AbsPath,
ignored_macros: &[Box<str>],
) -> ProcMacroLoadResult {
let res: Result<Vec<_>, String> = (|| {
let res: Result<Vec<_>, _> = (|| {
let dylib = MacroDylib::new(path.to_path_buf());
let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?;
let vec = server.load_dylib(dylib).map_err(|e| {
ProcMacroLoadingError::ProcMacroSrvError(format!("{e}").into_boxed_str())
})?;
if vec.is_empty() {
return Err("proc macro library returned no proc macros".to_owned());
return Err(ProcMacroLoadingError::NoProcMacros);
}
Ok(vec
.into_iter()
@ -412,7 +408,7 @@ pub fn load_proc_macro(
}
Err(e) => {
tracing::warn!("proc-macro loading for {path} failed: {e}");
Err((e, true))
Err(e)
}
}
}

View file

@ -7,8 +7,8 @@ use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync, thread};
use anyhow::Context;
use base_db::{
CrateBuilderId, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin,
CrateWorkspaceData, DependencyBuilder, Env, LangCrateOrigin, ProcMacroPaths,
TargetLayoutLoadResult,
CrateWorkspaceData, DependencyBuilder, Env, LangCrateOrigin, ProcMacroLoadingError,
ProcMacroPaths, TargetLayoutLoadResult,
};
use cfg::{CfgAtom, CfgDiff, CfgOptions};
use intern::{Symbol, sym};
@ -1641,11 +1641,11 @@ fn add_target_crate_root(
Some((BuildScriptOutput { proc_macro_dylib_path, .. }, has_errors)) => {
match proc_macro_dylib_path {
Some(path) => Ok((cargo_name.to_owned(), path.clone())),
None if has_errors => Err("failed to build proc-macro".to_owned()),
None => Err("proc-macro crate build data is missing dylib path".to_owned()),
None if has_errors => Err(ProcMacroLoadingError::FailedToBuild),
None => Err(ProcMacroLoadingError::MissingDylibPath),
}
}
None => Err("build scripts have not been built".to_owned()),
None => Err(ProcMacroLoadingError::NotYetBuilt),
};
proc_macros.insert(crate_id, proc_macro);
}

View file

@ -18,7 +18,7 @@ use std::{iter, mem};
use hir::{ChangeWithProcMacros, ProcMacrosBuilder, db::DefDatabase};
use ide_db::{
FxHashMap,
base_db::{CrateGraphBuilder, ProcMacroPaths, salsa::Durability},
base_db::{CrateGraphBuilder, ProcMacroLoadingError, ProcMacroPaths, salsa::Durability},
};
use itertools::Itertools;
use load_cargo::{ProjectFolders, load_proc_macro};
@ -438,9 +438,11 @@ impl GlobalState {
load_proc_macro(client, path, ignored_proc_macros)
}
Err(e) => Err((e.clone(), true)),
Err(e) => Err(e.clone()),
},
Err(ref e) => Err((e.clone(), true)),
Err(ref e) => Err(ProcMacroLoadingError::ProcMacroSrvError(
e.clone().into_boxed_str(),
)),
};
builder.insert(*crate_id, expansion_res)
}
@ -753,14 +755,14 @@ impl GlobalState {
change.set_proc_macros(
crate_graph
.iter()
.map(|id| (id, Err(("proc-macro has not been built yet".to_owned(), true))))
.map(|id| (id, Err(ProcMacroLoadingError::NotYetBuilt)))
.collect(),
);
} else {
change.set_proc_macros(
crate_graph
.iter()
.map(|id| (id, Err(("proc-macro expansion is disabled".to_owned(), false))))
.map(|id| (id, Err(ProcMacroLoadingError::Disabled)))
.collect(),
);
}