Salsify the crate graph

I.e. make it not one giant input but multiple, for incrementality and decreased memory usage for Salsa 3 reasons.
This commit is contained in:
Chayim Refael Friedman 2025-01-02 01:45:32 +02:00
parent 44f18c3d05
commit c94e9efbef
108 changed files with 3630 additions and 2512 deletions

View file

@ -1,7 +1,7 @@
//! A higher level attributes based on TokenTree, with also some shortcuts.
use std::{borrow::Cow, fmt, ops};
use base_db::CrateId;
use base_db::Crate;
use cfg::CfgExpr;
use either::Either;
use intern::{sym, Interned, Symbol};
@ -119,7 +119,7 @@ impl RawAttrs {
/// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
// FIXME: This should return a different type, signaling it was filtered?
pub fn filter(self, db: &dyn ExpandDatabase, krate: CrateId) -> RawAttrs {
pub fn filter(self, db: &dyn ExpandDatabase, krate: Crate) -> RawAttrs {
let has_cfg_attrs = self
.iter()
.any(|attr| attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr.clone()));
@ -127,7 +127,7 @@ impl RawAttrs {
return self;
}
let crate_graph = db.crate_graph();
let cfg_options = krate.cfg_options(db);
let new_attrs =
self.iter()
.flat_map(|attr| -> SmallVec<[_; 1]> {
@ -151,7 +151,6 @@ impl RawAttrs {
|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)),
);
let cfg_options = &crate_graph[krate].cfg_options;
let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg);
let cfg = CfgExpr::parse(&cfg);
if cfg_options.check(&cfg) == Some(false) {

View file

@ -231,7 +231,7 @@ fn assert_expand(
let cond = expect_fragment(
&mut iter,
parser::PrefixEntryPoint::Expr,
db.crate_graph()[id.lookup(db).krate].edition,
id.lookup(db).krate.data(db).edition,
tt.top_subtree().delimiter.delim_span(),
);
_ = iter.expect_char(',');
@ -333,7 +333,7 @@ fn cfg_expand(
) -> ExpandResult<tt::TopSubtree> {
let loc = db.lookup_intern_macro_call(id);
let expr = CfgExpr::parse(tt);
let enabled = db.crate_graph()[loc.krate].cfg_options.check(&expr) != Some(false);
let enabled = loc.krate.cfg_options(db).check(&expr) != Some(false);
let expanded = if enabled { quote!(span=>true) } else { quote!(span=>false) };
ExpandResult::ok(expanded)
}
@ -669,7 +669,7 @@ fn relative_file(
if res == call_site && !allow_recursion {
Err(ExpandError::other(err_span, format!("recursive inclusion of `{path_str}`")))
} else {
Ok(EditionedFileId::new(res, db.crate_graph()[lookup.krate].edition))
Ok(EditionedFileId::new(res, lookup.krate.data(db).edition))
}
}
@ -812,7 +812,7 @@ fn include_str_expand(
fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &Symbol) -> Option<String> {
let krate = db.lookup_intern_macro_call(arg_id).krate;
db.crate_graph()[krate].env.get(key.as_str())
krate.env(db).get(key.as_str())
}
fn env_expand(

View file

@ -1,7 +1,7 @@
//! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro
use std::iter::Peekable;
use base_db::CrateId;
use base_db::Crate;
use cfg::{CfgAtom, CfgExpr};
use intern::{sym, Symbol};
use rustc_hash::FxHashSet;
@ -13,16 +13,16 @@ use tracing::{debug, warn};
use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind};
fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option<bool> {
fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: Crate) -> Option<bool> {
if !attr.simple_name().as_deref().map(|v| v == "cfg")? {
return None;
}
let cfg = parse_from_attr_token_tree(&attr.meta()?.token_tree()?)?;
let enabled = db.crate_graph()[krate].cfg_options.check(&cfg) != Some(false);
let enabled = krate.cfg_options(db).check(&cfg) != Some(false);
Some(enabled)
}
fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option<bool> {
fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: Crate) -> Option<bool> {
if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? {
return None;
}
@ -32,17 +32,17 @@ fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Optio
pub fn check_cfg_attr_value(
db: &dyn ExpandDatabase,
attr: &TokenTree,
krate: CrateId,
krate: Crate,
) -> Option<bool> {
let cfg_expr = parse_from_attr_token_tree(attr)?;
let enabled = db.crate_graph()[krate].cfg_options.check(&cfg_expr) != Some(false);
let enabled = krate.cfg_options(db).check(&cfg_expr) != Some(false);
Some(enabled)
}
fn process_has_attrs_with_possible_comma<I: HasAttrs>(
db: &dyn ExpandDatabase,
items: impl Iterator<Item = I>,
krate: CrateId,
krate: Crate,
remove: &mut FxHashSet<SyntaxElement>,
) -> Option<()> {
for item in items {
@ -144,7 +144,7 @@ fn remove_possible_comma(item: &impl AstNode, res: &mut FxHashSet<SyntaxElement>
fn process_enum(
db: &dyn ExpandDatabase,
variants: VariantList,
krate: CrateId,
krate: Crate,
remove: &mut FxHashSet<SyntaxElement>,
) -> Option<()> {
'variant: for variant in variants.variants() {

View file

@ -1,17 +1,16 @@
//! Defines a unit of change that can applied to the database to get the next
//! state. Changes are transactional.
use base_db::{CrateGraph, CrateId, CrateWorkspaceData, FileChange, SourceRoot};
use rustc_hash::FxHashMap;
use base_db::{CrateGraphBuilder, FileChange, SourceRoot};
use salsa::Durability;
use span::FileId;
use triomphe::Arc;
use crate::{db::ExpandDatabase, proc_macro::ProcMacros};
use crate::{db::ExpandDatabase, proc_macro::ProcMacrosBuilder};
#[derive(Debug, Default)]
pub struct ChangeWithProcMacros {
pub source_change: FileChange,
pub proc_macros: Option<ProcMacros>,
pub proc_macros: Option<ProcMacrosBuilder>,
}
impl ChangeWithProcMacros {
@ -20,8 +19,13 @@ impl ChangeWithProcMacros {
}
pub fn apply(self, db: &mut impl ExpandDatabase) {
self.source_change.apply(db);
let crates_id_map = self.source_change.apply(db);
if let Some(proc_macros) = self.proc_macros {
let proc_macros = proc_macros.build(
crates_id_map
.as_ref()
.expect("cannot set proc macros without setting the crate graph too"),
);
db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH);
}
}
@ -30,16 +34,11 @@ impl ChangeWithProcMacros {
self.source_change.change_file(file_id, new_text)
}
pub fn set_crate_graph(
&mut self,
graph: CrateGraph,
ws_data: FxHashMap<CrateId, Arc<CrateWorkspaceData>>,
) {
pub fn set_crate_graph(&mut self, graph: CrateGraphBuilder) {
self.source_change.set_crate_graph(graph);
self.source_change.set_ws_data(ws_data);
}
pub fn set_proc_macros(&mut self, proc_macros: ProcMacros) {
pub fn set_proc_macros(&mut self, proc_macros: ProcMacrosBuilder) {
self.proc_macros = Some(proc_macros);
}

View file

@ -1,6 +1,6 @@
//! Defines database & queries for macro expansion.
use base_db::{CrateId, RootQueryDb};
use base_db::{Crate, RootQueryDb};
use either::Either;
use mbe::MatchedArmIndex;
use rustc_hash::FxHashSet;
@ -23,7 +23,7 @@ use crate::{
span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt,
SyntaxContextExt as _,
},
proc_macro::{CustomProcMacroExpander, ProcMacros},
proc_macro::{CrateProcMacros, CustomProcMacroExpander, ProcMacros},
span_map::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef},
tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
EagerExpander, ExpandError, ExpandResult, ExpandTo, MacroCallKind, MacroCallLoc, MacroDefId,
@ -57,10 +57,13 @@ pub enum TokenExpander {
#[query_group::query_group]
pub trait ExpandDatabase: RootQueryDb {
/// The proc macros.
/// The proc macros. Do not use this! Use `proc_macros_for_crate()` instead.
#[salsa::input]
fn proc_macros(&self) -> Arc<ProcMacros>;
#[salsa::invoke_actual(crate::proc_macro::proc_macros_for_crate)]
fn proc_macros_for_crate(&self, krate: Crate) -> Option<Arc<CrateProcMacros>>;
fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
#[salsa::transparent]
@ -120,7 +123,7 @@ pub trait ExpandDatabase: RootQueryDb {
#[salsa::invoke(DeclarativeMacroExpander::expander)]
fn decl_macro_expander(
&self,
def_crate: CrateId,
def_crate: Crate,
id: AstId<ast::Macro>,
) -> Arc<DeclarativeMacroExpander>;

View file

@ -1,6 +1,6 @@
//! Compiled declarative macro expanders (`macro_rules!` and `macro`)
use base_db::CrateId;
use base_db::Crate;
use intern::sym;
use span::{Edition, HirFileIdRepr, MacroCallId, Span, SyntaxContextId};
use stdx::TupleExt;
@ -70,7 +70,7 @@ impl DeclarativeMacroExpander {
pub(crate) fn expander(
db: &dyn ExpandDatabase,
def_crate: CrateId,
def_crate: Crate,
id: AstId<ast::Macro>,
) -> Arc<DeclarativeMacroExpander> {
let (root, map) = crate::db::parse_with_map(db, id.file_id);
@ -101,14 +101,12 @@ impl DeclarativeMacroExpander {
}
};
let ctx_edition = |ctx: SyntaxContextId| {
let crate_graph = db.crate_graph();
if ctx.is_root() {
crate_graph[def_crate].edition
def_crate.data(db).edition
} else {
// UNWRAP-SAFETY: Only the root context has no outer expansion
let krate = db.lookup_intern_macro_call(ctx.outer_expn(db).unwrap()).def.krate;
crate_graph[krate].edition
krate.data(db).edition
}
};
let (mac, transparency) = match id.to_ptr(db).to_node(&root) {

View file

@ -18,7 +18,7 @@
//!
//!
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
use base_db::CrateId;
use base_db::Crate;
use span::SyntaxContextId;
use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent};
use syntax_bridge::DocCommentDesugarMode;
@ -34,7 +34,7 @@ use crate::{
pub fn expand_eager_macro_input(
db: &dyn ExpandDatabase,
krate: CrateId,
krate: Crate,
macro_call: &ast::MacroCall,
ast_id: AstId<ast::MacroCall>,
def: MacroDefId,
@ -115,7 +115,7 @@ fn lazy_expand(
def: &MacroDefId,
macro_call: &ast::MacroCall,
ast_id: AstId<ast::MacroCall>,
krate: CrateId,
krate: Crate,
call_site: SyntaxContextId,
) -> ExpandResult<(InFile<Parse<SyntaxNode>>, Arc<ExpansionSpanMap>)> {
let expand_to = ExpandTo::from_call_site(macro_call);
@ -137,7 +137,7 @@ fn eager_macro_recur(
expanded_map: &mut ExpansionSpanMap,
mut offset: TextSize,
curr: InFile<SyntaxNode>,
krate: CrateId,
krate: Crate,
call_site: SyntaxContextId,
macro_resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>,
) -> ExpandResult<Option<(SyntaxNode, TextSize)>> {
@ -176,7 +176,7 @@ fn eager_macro_recur(
Some(path) => match macro_resolver(&path) {
Some(def) => def,
None => {
let edition = db.crate_graph()[krate].edition;
let edition = krate.data(db).edition;
error = Some(ExpandError::other(
span_map.span_at(call.syntax().text_range().start()),
format!("unresolved macro {}", path.display(db, edition)),

View file

@ -33,7 +33,7 @@ use triomphe::Arc;
use core::fmt;
use std::hash::Hash;
use base_db::CrateId;
use base_db::Crate;
use either::Either;
use span::{
Edition, EditionedFileId, ErasedFileAstId, FileAstId, HirFileIdRepr, Span, SpanAnchor,
@ -157,7 +157,7 @@ impl ExpandError {
pub enum ExpandErrorKind {
/// Attribute macro expansion is disabled.
ProcMacroAttrExpansionDisabled,
MissingProcMacroExpander(CrateId),
MissingProcMacroExpander(Crate),
/// The macro for this call is disabled.
MacroDisabled,
/// The macro definition has errors.
@ -200,7 +200,7 @@ impl ExpandErrorKind {
kind: RenderedExpandError::DISABLED,
},
&ExpandErrorKind::MissingProcMacroExpander(def_crate) => {
match db.proc_macros().get_error_for_crate(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,
@ -250,14 +250,14 @@ impl From<mbe::ExpandError> for ExpandError {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MacroCallLoc {
pub def: MacroDefId,
pub krate: CrateId,
pub krate: Crate,
pub kind: MacroCallKind,
pub ctxt: SyntaxContextId,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MacroDefId {
pub krate: CrateId,
pub krate: Crate,
pub edition: Edition,
pub kind: MacroDefKind,
pub local_inner: bool,
@ -525,7 +525,7 @@ impl MacroDefId {
pub fn make_call(
self,
db: &dyn ExpandDatabase,
krate: CrateId,
krate: Crate,
kind: MacroCallKind,
ctxt: SyntaxContextId,
) -> MacroCallId {

View file

@ -11,7 +11,7 @@ use crate::{
name::{AsName, Name},
tt,
};
use base_db::CrateId;
use base_db::Crate;
use intern::sym;
use smallvec::SmallVec;
use span::{Edition, SyntaxContextId};
@ -33,7 +33,7 @@ pub enum PathKind {
Abs,
// FIXME: Can we remove this somehow?
/// `$crate` from macro expansion
DollarCrate(CrateId),
DollarCrate(Crate),
}
impl PathKind {
@ -333,7 +333,7 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Optio
Some(ModPath { kind, segments })
}
pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContextId) -> Option<CrateId> {
pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContextId) -> Option<Crate> {
// When resolving `$crate` from a `macro_rules!` invoked in a `macro`,
// we don't want to pretend that the `macro_rules!` definition is in the `macro`
// as described in `SyntaxContextId::apply_mark`, so we ignore prepended opaque marks.

View file

@ -260,7 +260,7 @@ impl AsName for ast::FieldKind {
}
}
impl AsName for base_db::Dependency {
impl AsName for base_db::BuiltDependency {
fn as_name(&self) -> Name {
Name::new_symbol_root((*self.name).clone())
}

View file

@ -1,6 +1,6 @@
//! Pretty printing of macros output.
use base_db::CrateId;
use base_db::Crate;
use rustc_hash::FxHashMap;
use syntax::NodeOrToken;
use syntax::{ast::make, SyntaxNode};
@ -13,13 +13,12 @@ pub fn prettify_macro_expansion(
db: &dyn ExpandDatabase,
syn: SyntaxNode,
span_map: &ExpansionSpanMap,
target_crate_id: CrateId,
target_crate_id: Crate,
) -> SyntaxNode {
// Because `syntax_bridge::prettify_macro_expansion::prettify_macro_expansion()` clones subtree for `syn`,
// that means it will be offsetted to the beginning.
let span_offset = syn.text_range().start();
let crate_graph = db.crate_graph();
let target_crate = &crate_graph[target_crate_id];
let target_crate = target_crate_id.data(db);
let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default();
syntax_bridge::prettify_macro_expansion::prettify_macro_expansion(syn, &mut |dollar_crate| {
let ctx = span_map.span_at(dollar_crate.text_range().start() + span_offset).ctx;
@ -41,7 +40,7 @@ pub fn prettify_macro_expansion(
target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate)
{
make::tokens::ident(dep.name.as_str())
} else if let Some(crate_name) = &crate_graph[macro_def_crate].display_name {
} 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();

View file

@ -1,24 +1,36 @@
//! Proc Macro Expander stuff
use core::fmt;
use std::any::Any;
use std::{panic::RefUnwindSafe, sync};
use base_db::{CrateId, Env};
use base_db::{Crate, CrateBuilderId, CratesIdMap, Env};
use intern::Symbol;
use rustc_hash::FxHashMap;
use span::Span;
use triomphe::Arc;
use crate::{db::ExpandDatabase, tt, ExpandError, ExpandErrorKind, ExpandResult};
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Hash)]
pub enum ProcMacroKind {
CustomDerive,
Bang,
Attr,
}
pub trait AsAny: Any {
fn as_any(&self) -> &dyn Any;
}
impl<T: Any> AsAny for T {
fn as_any(&self) -> &dyn Any {
self
}
}
/// A proc-macro expander implementation.
pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + AsAny {
/// Run the expander with the given input subtree, optional attribute input subtree (for
/// [`ProcMacroKind::Attr`]), environment variables, and span information.
fn expand(
@ -31,8 +43,18 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
mixed_site: Span,
current_dir: Option<String>,
) -> Result<tt::TopSubtree, ProcMacroExpansionError>;
fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool;
}
impl PartialEq for dyn ProcMacroExpander {
fn eq(&self, other: &Self) -> bool {
self.eq_dyn(other)
}
}
impl Eq for dyn ProcMacroExpander {}
#[derive(Debug)]
pub enum ProcMacroExpansionError {
/// The proc-macro panicked.
@ -45,41 +67,68 @@ pub type ProcMacroLoadResult = Result<Vec<ProcMacro>, (String, bool)>;
type StoredProcMacroLoadResult = Result<Box<[ProcMacro]>, (Box<str>, bool)>;
#[derive(Default, Debug)]
pub struct ProcMacrosBuilder(FxHashMap<CrateId, StoredProcMacroLoadResult>);
pub struct ProcMacrosBuilder(FxHashMap<CrateBuilderId, Arc<CrateProcMacros>>);
impl ProcMacrosBuilder {
pub fn insert(&mut self, proc_macros_crate: CrateId, proc_macro: ProcMacroLoadResult) {
pub fn insert(
&mut self,
proc_macros_crate: CrateBuilderId,
mut proc_macro: ProcMacroLoadResult,
) {
if let Ok(proc_macros) = &mut proc_macro {
// Sort proc macros to improve incrementality when only their order has changed (ideally the build system
// will not change their order, but just to be sure).
proc_macros
.sort_unstable_by_key(|proc_macro| (proc_macro.name.clone(), proc_macro.kind));
}
self.0.insert(
proc_macros_crate,
match proc_macro {
Ok(it) => Ok(it.into_boxed_slice()),
Err((e, hard_err)) => Err((e.into_boxed_str(), hard_err)),
Ok(it) => Arc::new(CrateProcMacros(Ok(it.into_boxed_slice()))),
Err((e, hard_err)) => {
Arc::new(CrateProcMacros(Err((e.into_boxed_str(), hard_err))))
}
},
);
}
pub fn build(mut self) -> ProcMacros {
self.0.shrink_to_fit();
ProcMacros(self.0)
pub(crate) fn build(self, crates_id_map: &CratesIdMap) -> ProcMacros {
let mut map = self
.0
.into_iter()
.map(|(krate, proc_macro)| (crates_id_map[&krate], proc_macro))
.collect::<FxHashMap<_, _>>();
map.shrink_to_fit();
ProcMacros(map)
}
}
#[derive(Default, Debug)]
pub struct ProcMacros(FxHashMap<CrateId, StoredProcMacroLoadResult>);
impl FromIterator<(CrateId, ProcMacroLoadResult)> for ProcMacros {
fn from_iter<T: IntoIterator<Item = (CrateId, ProcMacroLoadResult)>>(iter: T) -> Self {
impl FromIterator<(CrateBuilderId, ProcMacroLoadResult)> for ProcMacrosBuilder {
fn from_iter<T: IntoIterator<Item = (CrateBuilderId, ProcMacroLoadResult)>>(iter: T) -> Self {
let mut builder = ProcMacrosBuilder::default();
for (k, v) in iter {
builder.insert(k, v);
}
builder.build()
builder
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct CrateProcMacros(StoredProcMacroLoadResult);
#[derive(Default, Debug)]
pub struct ProcMacros(FxHashMap<Crate, Arc<CrateProcMacros>>);
impl ProcMacros {
fn get(&self, krate: CrateId, idx: u32, err_span: Span) -> Result<&ProcMacro, ExpandError> {
let proc_macros = match self.0.get(&krate) {
Some(Ok(proc_macros)) => proc_macros,
Some(Err(_)) | None => {
fn get(&self, krate: Crate) -> Option<Arc<CrateProcMacros>> {
self.0.get(&krate).cloned()
}
}
impl CrateProcMacros {
fn get(&self, idx: u32, err_span: Span) -> Result<&ProcMacro, ExpandError> {
let proc_macros = match &self.0 {
Ok(proc_macros) => proc_macros,
Err(_) => {
return Err(ExpandError::other(
err_span,
"internal error: no proc macros for crate",
@ -98,18 +147,17 @@ impl ProcMacros {
)
}
pub fn get_error_for_crate(&self, krate: CrateId) -> Option<(&str, bool)> {
self.0.get(&krate).and_then(|it| it.as_ref().err()).map(|(e, hard_err)| (&**e, *hard_err))
pub fn get_error(&self) -> Option<(&str, bool)> {
self.0.as_ref().err().map(|(e, hard_err)| (&**e, *hard_err))
}
/// Fetch the [`CustomProcMacroExpander`]s and their corresponding names for the given crate.
pub fn for_crate(
pub fn list(
&self,
krate: CrateId,
def_site_ctx: span::SyntaxContextId,
) -> Option<Box<[(crate::name::Name, CustomProcMacroExpander, bool)]>> {
match self.0.get(&krate) {
Some(Ok(proc_macros)) => Some({
match &self.0 {
Ok(proc_macros) => Some(
proc_macros
.iter()
.enumerate()
@ -117,15 +165,15 @@ impl ProcMacros {
let name = crate::name::Name::new_symbol(it.name.clone(), def_site_ctx);
(name, CustomProcMacroExpander::new(idx as u32), it.disabled)
})
.collect()
}),
.collect(),
),
_ => None,
}
}
}
/// A loaded proc-macro.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq)]
pub struct ProcMacro {
/// The name of the proc macro.
pub name: Symbol,
@ -137,6 +185,23 @@ pub struct ProcMacro {
pub disabled: bool,
}
// `#[derive(PartialEq)]` generates a strange "cannot move" error.
impl PartialEq for ProcMacro {
fn eq(&self, other: &Self) -> bool {
let Self { name, kind, expander, disabled } = self;
let Self {
name: other_name,
kind: other_kind,
expander: other_expander,
disabled: other_disabled,
} = other;
name == other_name
&& kind == other_kind
&& expander == other_expander
&& disabled == other_disabled
}
}
/// A custom proc-macro expander handle. This handle together with its crate resolves to a [`ProcMacro`]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct CustomProcMacroExpander {
@ -187,7 +252,7 @@ impl CustomProcMacroExpander {
}
/// The macro is explicitly disabled due to proc-macro attribute expansion being disabled.
pub fn as_expand_error(&self, def_crate: CrateId) -> Option<ExpandErrorKind> {
pub fn as_expand_error(&self, def_crate: Crate) -> Option<ExpandErrorKind> {
match self.proc_macro_id {
Self::PROC_MACRO_ATTR_DISABLED => Some(ExpandErrorKind::ProcMacroAttrExpansionDisabled),
Self::DISABLED_ID => Some(ExpandErrorKind::MacroDisabled),
@ -199,8 +264,8 @@ impl CustomProcMacroExpander {
pub fn expand(
self,
db: &dyn ExpandDatabase,
def_crate: CrateId,
calling_crate: CrateId,
def_crate: Crate,
calling_crate: Crate,
tt: &tt::TopSubtree,
attr_arg: Option<&tt::TopSubtree>,
def_site: Span,
@ -221,8 +286,22 @@ impl CustomProcMacroExpander {
ExpandError::new(call_site, ExpandErrorKind::MacroDisabled),
),
id => {
let proc_macros = db.proc_macros();
let proc_macro = match proc_macros.get(def_crate, id, call_site) {
let proc_macros = match db.proc_macros_for_crate(def_crate) {
Some(it) => it,
None => {
return ExpandResult::new(
tt::TopSubtree::empty(tt::DelimSpan {
open: call_site,
close: call_site,
}),
ExpandError::other(
call_site,
"internal error: no proc macros for crate",
),
)
}
};
let proc_macro = match proc_macros.get(id, call_site) {
Ok(proc_macro) => proc_macro,
Err(e) => {
return ExpandResult::new(
@ -235,11 +314,10 @@ impl CustomProcMacroExpander {
}
};
let krate_graph = db.crate_graph();
// Proc macros have access to the environment variables of the invoking crate.
let env = &krate_graph[calling_crate].env;
let env = calling_crate.env(db);
let current_dir =
krate_graph[calling_crate].proc_macro_cwd.as_deref().map(ToString::to_string);
calling_crate.data(db).proc_macro_cwd.as_deref().map(ToString::to_string);
match proc_macro.expander.expand(
tt,
@ -278,3 +356,10 @@ impl CustomProcMacroExpander {
}
}
}
pub(crate) fn proc_macros_for_crate(
db: &dyn ExpandDatabase,
krate: Crate,
) -> Option<Arc<CrateProcMacros>> {
db.proc_macros().get(krate)
}