Encode disabled proc-macros via boolean flag, not special Expander

This commit is contained in:
Lukas Wirth 2024-02-12 13:34:38 +01:00
parent ab50ec9863
commit e2a985e93f
8 changed files with 91 additions and 148 deletions

View file

@ -653,7 +653,8 @@ impl<'a> AssocItemCollector<'a> {
)); ));
continue 'attrs; continue 'attrs;
} else if exp.is_disabled() { }
if exp.is_disabled() {
continue 'attrs; continue 'attrs;
} }
} }

View file

@ -58,6 +58,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
name: "identity_when_valid".into(), name: "identity_when_valid".into(),
kind: ProcMacroKind::Attr, kind: ProcMacroKind::Attr,
expander: sync::Arc::new(IdentityWhenValidProcMacroExpander), expander: sync::Arc::new(IdentityWhenValidProcMacroExpander),
disabled: false,
}, },
)]; )];
let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros); let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros);

View file

@ -11,7 +11,7 @@ use either::Either;
use hir_expand::{ use hir_expand::{
ast_id_map::FileAstId, ast_id_map::FileAstId,
attrs::{Attr, AttrId}, attrs::{Attr, AttrId},
builtin_attr_macro::find_builtin_attr, builtin_attr_macro::{find_builtin_attr, BuiltinAttrExpander},
builtin_derive_macro::find_builtin_derive, builtin_derive_macro::find_builtin_derive,
builtin_fn_macro::find_builtin_macro, builtin_fn_macro::find_builtin_macro,
name::{name, AsName, Name}, name::{name, AsName, Name},
@ -98,12 +98,12 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
}; };
( (
name.as_name(), name.as_name(),
if it.expander.should_expand() { if it.disabled {
CustomProcMacroExpander::disabled()
} else {
CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId( CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId(
idx as u32, idx as u32,
)) ))
} else {
CustomProcMacroExpander::disabled()
}, },
) )
}) })
@ -608,9 +608,6 @@ impl DefCollector<'_> {
id: ItemTreeId<item_tree::Function>, id: ItemTreeId<item_tree::Function>,
fn_id: FunctionId, fn_id: FunctionId,
) { ) {
if self.def_map.block.is_some() {
return;
}
let kind = def.kind.to_basedb_kind(); let kind = def.kind.to_basedb_kind();
let (expander, kind) = let (expander, kind) =
match self.proc_macros.as_ref().map(|it| it.iter().find(|(n, _)| n == &def.name)) { match self.proc_macros.as_ref().map(|it| it.iter().find(|(n, _)| n == &def.name)) {
@ -1124,9 +1121,16 @@ impl DefCollector<'_> {
let mut push_resolved = |directive: &MacroDirective, call_id| { let mut push_resolved = |directive: &MacroDirective, call_id| {
resolved.push((directive.module_id, directive.depth, directive.container, call_id)); resolved.push((directive.module_id, directive.depth, directive.container, call_id));
}; };
#[derive(PartialEq, Eq)]
enum Resolved {
Yes,
No,
}
let mut res = ReachedFixedPoint::Yes; let mut res = ReachedFixedPoint::Yes;
// Retain unresolved macros after this round of resolution. // Retain unresolved macros after this round of resolution.
macros.retain(|directive| { let mut retain = |directive: &MacroDirective| {
let subns = match &directive.kind { let subns = match &directive.kind {
MacroDirectiveKind::FnLike { .. } => MacroSubNs::Bang, MacroDirectiveKind::FnLike { .. } => MacroSubNs::Bang,
MacroDirectiveKind::Attr { .. } | MacroDirectiveKind::Derive { .. } => { MacroDirectiveKind::Attr { .. } | MacroDirectiveKind::Derive { .. } => {
@ -1161,34 +1165,10 @@ impl DefCollector<'_> {
.scope .scope
.add_macro_invoc(ast_id.ast_id, call_id); .add_macro_invoc(ast_id.ast_id, call_id);
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
if let MacroDefKind::ProcMacro(expander, _, _) = loc.def.kind {
if expander.is_dummy() {
// If there's no expander for the proc macro (e.g.
// because proc macros are disabled, or building the
// proc macro crate failed), report this and skip
// expansion like we would if it was disabled
self.def_map.diagnostics.push(
DefDiagnostic::unresolved_proc_macro(
directive.module_id,
loc.kind,
loc.def.krate,
),
);
res = ReachedFixedPoint::No;
return false;
} else if expander.is_disabled() {
res = ReachedFixedPoint::No;
return false;
}
}
push_resolved(directive, call_id); push_resolved(directive, call_id);
res = ReachedFixedPoint::No; res = ReachedFixedPoint::No;
return false; return Resolved::Yes;
} }
} }
MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, call_site } => { MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, call_site } => {
@ -1227,7 +1207,7 @@ impl DefCollector<'_> {
push_resolved(directive, call_id); push_resolved(directive, call_id);
res = ReachedFixedPoint::No; res = ReachedFixedPoint::No;
return false; return Resolved::Yes;
} }
} }
MacroDirectiveKind::Attr { ast_id: file_ast_id, mod_item, attr, tree } => { MacroDirectiveKind::Attr { ast_id: file_ast_id, mod_item, attr, tree } => {
@ -1250,7 +1230,7 @@ impl DefCollector<'_> {
} }
.collect(&[*mod_item], directive.container); .collect(&[*mod_item], directive.container);
res = ReachedFixedPoint::No; res = ReachedFixedPoint::No;
false Resolved::Yes
}; };
if let Some(ident) = path.as_ident() { if let Some(ident) = path.as_ident() {
@ -1266,13 +1246,18 @@ impl DefCollector<'_> {
let def = match resolver_def_id(path.clone()) { let def = match resolver_def_id(path.clone()) {
Some(def) if def.is_attribute() => def, Some(def) if def.is_attribute() => def,
_ => return true, _ => return Resolved::No,
}; };
if matches!(
def, if let MacroDefId {
MacroDefId { kind: MacroDefKind::BuiltInAttr(expander, _),.. } kind:
if expander.is_derive() MacroDefKind::BuiltInAttr(
) { BuiltinAttrExpander::Derive | BuiltinAttrExpander::DeriveConst,
_,
),
..
} = def
{
// Resolved to `#[derive]`, we don't actually expand this attribute like // Resolved to `#[derive]`, we don't actually expand this attribute like
// normal (as that would just be an identity expansion with extra output) // normal (as that would just be an identity expansion with extra output)
// Instead we treat derive attributes special and apply them separately. // Instead we treat derive attributes special and apply them separately.
@ -1345,16 +1330,6 @@ impl DefCollector<'_> {
let call_id = let call_id =
attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def); attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def);
// If proc attribute macro expansion is disabled, skip expanding it here
if !self.db.expand_proc_attr_macros() {
self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
directive.module_id,
self.db.lookup_intern_macro_call(call_id).kind,
def.krate,
));
return recollect_without(self);
}
// Skip #[test]/#[bench] expansion, which would merely result in more memory usage // Skip #[test]/#[bench] expansion, which would merely result in more memory usage
// due to duplicating functions into macro expansions // due to duplicating functions into macro expansions
if matches!( if matches!(
@ -1366,19 +1341,29 @@ impl DefCollector<'_> {
} }
if let MacroDefKind::ProcMacro(exp, ..) = def.kind { if let MacroDefKind::ProcMacro(exp, ..) = def.kind {
if exp.is_dummy() { // If proc attribute macro expansion is disabled, skip expanding it here
// If there's no expander for the proc macro (e.g. if !self.db.expand_proc_attr_macros() {
// because proc macros are disabled, or building the
// proc macro crate failed), report this and skip
// expansion like we would if it was disabled
self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro( self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
directive.module_id, directive.module_id,
self.db.lookup_intern_macro_call(call_id).kind, self.db.lookup_intern_macro_call(call_id).kind,
def.krate, def.krate,
)); ));
return recollect_without(self); return recollect_without(self);
} else if exp.is_disabled() { }
// If there's no expander for the proc macro (e.g.
// because proc macros are disabled, or building the
// proc macro crate failed), report this and skip
// expansion like we would if it was disabled
if exp.is_dummy() {
self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
directive.module_id,
self.db.lookup_intern_macro_call(call_id).kind,
def.krate,
));
return recollect_without(self);
}
if exp.is_disabled() {
return recollect_without(self); return recollect_without(self);
} }
} }
@ -1389,12 +1374,13 @@ impl DefCollector<'_> {
push_resolved(directive, call_id); push_resolved(directive, call_id);
res = ReachedFixedPoint::No; res = ReachedFixedPoint::No;
return false; return Resolved::Yes;
} }
} }
true Resolved::No
}); };
macros.retain(|it| retain(it) == Resolved::No);
// Attribute resolution can add unresolved macro invocations, so concatenate the lists. // Attribute resolution can add unresolved macro invocations, so concatenate the lists.
macros.extend(mem::take(&mut self.unresolved_macros)); macros.extend(mem::take(&mut self.unresolved_macros));
self.unresolved_macros = macros; self.unresolved_macros = macros;
@ -1704,7 +1690,11 @@ impl ModCollector<'_, '_> {
FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db); FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db);
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]); let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
if self.def_collector.is_proc_macro && self.module_id == DefMap::ROOT {
if self.def_collector.def_map.block.is_none()
&& self.def_collector.is_proc_macro
&& self.module_id == DefMap::ROOT
{
if let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) { if let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) {
self.def_collector.export_proc_macro( self.def_collector.export_proc_macro(
proc_macro, proc_macro,

View file

@ -103,6 +103,9 @@ impl DefDiagnostic {
} }
// FIXME: Whats the difference between this and unresolved_macro_call // FIXME: Whats the difference between this and unresolved_macro_call
// 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(crate) fn unresolved_proc_macro(
container: LocalModuleId, container: LocalModuleId,
ast: MacroCallKind, ast: MacroCallKind,

View file

@ -31,16 +31,6 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
call_site: Span, call_site: Span,
mixed_site: Span, mixed_site: Span,
) -> Result<tt::Subtree, ProcMacroExpansionError>; ) -> Result<tt::Subtree, ProcMacroExpansionError>;
/// If this returns `false`, expansions via [`expand`](ProcMacroExpander::expand) will always
/// return the input subtree or will always return an error.
///
/// This is used to skip any additional expansion-related work,
/// e.g. to make sure we do not touch the syntax tree in any way
/// if a proc macro will never be expanded.
fn should_expand(&self) -> bool {
true
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -59,6 +49,7 @@ pub struct ProcMacro {
pub name: SmolStr, pub name: SmolStr,
pub kind: ProcMacroKind, pub kind: ProcMacroKind,
pub expander: sync::Arc<dyn ProcMacroExpander>, pub expander: sync::Arc<dyn ProcMacroExpander>,
pub disabled: bool,
} }
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
@ -66,31 +57,35 @@ pub struct CustomProcMacroExpander {
proc_macro_id: ProcMacroId, proc_macro_id: ProcMacroId,
} }
impl CustomProcMacroExpander {
const DUMMY_ID: u32 = !0; const DUMMY_ID: u32 = !0;
const DISABLED_ID: u32 = !1; const DISABLED_ID: u32 = !1;
impl CustomProcMacroExpander {
pub fn new(proc_macro_id: ProcMacroId) -> Self { pub fn new(proc_macro_id: ProcMacroId) -> Self {
assert_ne!(proc_macro_id.0, DUMMY_ID); assert_ne!(proc_macro_id.0, Self::DUMMY_ID);
assert_ne!(proc_macro_id.0, Self::DISABLED_ID);
Self { proc_macro_id } Self { proc_macro_id }
} }
pub fn dummy() -> Self { /// A dummy expander that always errors. This is used for proc-macros that are missing, usually
Self { proc_macro_id: ProcMacroId(DUMMY_ID) } /// due to them not being built yet.
pub const fn dummy() -> Self {
Self { proc_macro_id: ProcMacroId(Self::DUMMY_ID) }
} }
/// The macro was not yet resolved. /// The macro was not yet resolved.
pub fn is_dummy(&self) -> bool { pub const fn is_dummy(&self) -> bool {
self.proc_macro_id.0 == DUMMY_ID self.proc_macro_id.0 == Self::DUMMY_ID
} }
pub fn disabled() -> Self { /// A dummy expander that always errors. This expander is used for macros that have been disabled.
Self { proc_macro_id: ProcMacroId(DISABLED_ID) } pub const fn disabled() -> Self {
Self { proc_macro_id: ProcMacroId(Self::DISABLED_ID) }
} }
/// The macro is explicitly disabled and cannot be expanded. /// The macro is explicitly disabled and cannot be expanded.
pub fn is_disabled(&self) -> bool { pub const fn is_disabled(&self) -> bool {
self.proc_macro_id.0 == DISABLED_ID self.proc_macro_id.0 == Self::DISABLED_ID
} }
pub fn expand( pub fn expand(
@ -105,11 +100,11 @@ impl CustomProcMacroExpander {
mixed_site: Span, mixed_site: Span,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
match self.proc_macro_id { match self.proc_macro_id {
ProcMacroId(DUMMY_ID) => ExpandResult::new( ProcMacroId(Self::DUMMY_ID) => ExpandResult::new(
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
ExpandError::UnresolvedProcMacro(def_crate), ExpandError::UnresolvedProcMacro(def_crate),
), ),
ProcMacroId(DISABLED_ID) => ExpandResult::new( ProcMacroId(Self::DISABLED_ID) => ExpandResult::new(
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
ExpandError::MacroDisabled, ExpandError::MacroDisabled,
), ),
@ -135,7 +130,7 @@ impl CustomProcMacroExpander {
); );
return ExpandResult::new( return ExpandResult::new(
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
ExpandError::other("Internal error"), ExpandError::other("Internal error: proc-macro index out of bounds"),
); );
} }
}; };

View file

@ -18,7 +18,6 @@ use itertools::Itertools;
use proc_macro_api::{MacroDylib, ProcMacroServer}; use proc_macro_api::{MacroDylib, ProcMacroServer};
use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace}; use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace};
use span::Span; use span::Span;
use tt::DelimSpan;
use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf, VfsPath}; use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf, VfsPath};
pub struct LoadCargoConfig { pub struct LoadCargoConfig {
@ -357,16 +356,8 @@ fn expander_to_proc_macro(
proc_macro_api::ProcMacroKind::FuncLike => ProcMacroKind::FuncLike, proc_macro_api::ProcMacroKind::FuncLike => ProcMacroKind::FuncLike,
proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr, proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr,
}; };
let expander: sync::Arc<dyn ProcMacroExpander> = let disabled = ignored_macros.iter().any(|replace| **replace == name);
if ignored_macros.iter().any(|replace| &**replace == name) { ProcMacro { name, kind, expander: sync::Arc::new(Expander(expander)), disabled }
match kind {
ProcMacroKind::Attr => sync::Arc::new(IdentityExpander),
_ => sync::Arc::new(EmptyExpander),
}
} else {
sync::Arc::new(Expander(expander))
};
ProcMacro { name, kind, expander }
} }
#[derive(Debug)] #[derive(Debug)]
@ -391,48 +382,6 @@ impl ProcMacroExpander for Expander {
} }
} }
/// Dummy identity expander, used for attribute proc-macros that are deliberately ignored by the user.
#[derive(Debug)]
struct IdentityExpander;
impl ProcMacroExpander for IdentityExpander {
fn expand(
&self,
subtree: &tt::Subtree<Span>,
_: Option<&tt::Subtree<Span>>,
_: &Env,
_: Span,
_: Span,
_: Span,
) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> {
Ok(subtree.clone())
}
fn should_expand(&self) -> bool {
false
}
}
/// Empty expander, used for proc-macros that are deliberately ignored by the user.
#[derive(Debug)]
struct EmptyExpander;
impl ProcMacroExpander for EmptyExpander {
fn expand(
&self,
_: &tt::Subtree<Span>,
_: Option<&tt::Subtree<Span>>,
_: &Env,
call_site: Span,
_: Span,
_: Span,
) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> {
Ok(tt::Subtree::empty(DelimSpan { open: call_site, close: call_site }))
}
fn should_expand(&self) -> bool {
false
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ide_db::base_db::SourceDatabase; use ide_db::base_db::SourceDatabase;

View file

@ -333,13 +333,12 @@ impl GlobalState {
crate_name crate_name
.as_deref() .as_deref()
.and_then(|crate_name| { .and_then(|crate_name| {
ignored_proc_macros.iter().find_map(|c| { ignored_proc_macros.iter().find_map(
if eq_ignore_underscore(&*c.0, crate_name) { |(name, macros)| {
Some(&**c.1) eq_ignore_underscore(name, crate_name)
} else { .then_some(&**macros)
None },
} )
})
}) })
.unwrap_or_default(), .unwrap_or_default(),
) )

View file

@ -374,6 +374,7 @@ pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
name: "identity".into(), name: "identity".into(),
kind: ProcMacroKind::Attr, kind: ProcMacroKind::Attr,
expander: sync::Arc::new(IdentityProcMacroExpander), expander: sync::Arc::new(IdentityProcMacroExpander),
disabled: false,
}, },
), ),
( (
@ -388,6 +389,7 @@ pub fn derive_identity(item: TokenStream) -> TokenStream {
name: "DeriveIdentity".into(), name: "DeriveIdentity".into(),
kind: ProcMacroKind::CustomDerive, kind: ProcMacroKind::CustomDerive,
expander: sync::Arc::new(IdentityProcMacroExpander), expander: sync::Arc::new(IdentityProcMacroExpander),
disabled: false,
}, },
), ),
( (
@ -402,6 +404,7 @@ pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream {
name: "input_replace".into(), name: "input_replace".into(),
kind: ProcMacroKind::Attr, kind: ProcMacroKind::Attr,
expander: sync::Arc::new(AttributeInputReplaceProcMacroExpander), expander: sync::Arc::new(AttributeInputReplaceProcMacroExpander),
disabled: false,
}, },
), ),
( (
@ -416,6 +419,7 @@ pub fn mirror(input: TokenStream) -> TokenStream {
name: "mirror".into(), name: "mirror".into(),
kind: ProcMacroKind::FuncLike, kind: ProcMacroKind::FuncLike,
expander: sync::Arc::new(MirrorProcMacroExpander), expander: sync::Arc::new(MirrorProcMacroExpander),
disabled: false,
}, },
), ),
( (
@ -430,6 +434,7 @@ pub fn shorten(input: TokenStream) -> TokenStream {
name: "shorten".into(), name: "shorten".into(),
kind: ProcMacroKind::FuncLike, kind: ProcMacroKind::FuncLike,
expander: sync::Arc::new(ShortenProcMacroExpander), expander: sync::Arc::new(ShortenProcMacroExpander),
disabled: false,
}, },
), ),
] ]