Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/base-db/src/input.rs1
-rw-r--r--crates/hir-def/src/data.rs12
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mod.rs1
-rw-r--r--crates/hir-def/src/nameres/collector.rs93
-rw-r--r--crates/hir-def/src/nameres/diagnostics.rs3
-rw-r--r--crates/hir-expand/src/lib.rs3
-rw-r--r--crates/hir-expand/src/proc_macro.rs38
-rw-r--r--crates/load-cargo/src/lib.rs55
-rw-r--r--crates/rust-analyzer/src/config.rs2
-rw-r--r--crates/rust-analyzer/src/reload.rs26
-rw-r--r--crates/test-fixture/src/lib.rs5
11 files changed, 139 insertions, 100 deletions
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index 9560826e37..1db8c2f29d 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -243,6 +243,7 @@ impl CrateDisplayName {
CrateDisplayName { crate_name, canonical_name }
}
}
+
pub type TargetLayoutLoadResult = Result<Arc<str>, Arc<str>>;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index 7ce05b64d0..f506864902 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -634,7 +634,6 @@ impl<'a> AssocItemCollector<'a> {
attr,
) {
Ok(ResolvedAttr::Macro(call_id)) => {
- self.attr_calls.push((ast_id, call_id));
// If proc attribute macro expansion is disabled, skip expanding it here
if !self.db.expand_proc_attr_macros() {
continue 'attrs;
@@ -647,10 +646,21 @@ impl<'a> AssocItemCollector<'a> {
// disabled. This is analogous to the handling in
// `DefCollector::collect_macros`.
if exp.is_dummy() {
+ self.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
+ self.module_id.local_id,
+ loc.kind,
+ loc.def.krate,
+ ));
+
+ continue 'attrs;
+ }
+ if exp.is_disabled() {
continue 'attrs;
}
}
+ self.attr_calls.push((ast_id, call_id));
+
let res =
self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
self.collect_macro_items(res, &|| loc.kind.clone());
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index fc5a6e80a4..23b10cfd8e 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -58,6 +58,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
name: "identity_when_valid".into(),
kind: ProcMacroKind::Attr,
expander: sync::Arc::new(IdentityWhenValidProcMacroExpander),
+ disabled: false,
},
)];
let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros);
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 21cc28f1b3..8f64df280c 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -11,7 +11,7 @@ use either::Either;
use hir_expand::{
ast_id_map::FileAstId,
attrs::{Attr, AttrId},
- builtin_attr_macro::find_builtin_attr,
+ builtin_attr_macro::{find_builtin_attr, BuiltinAttrExpander},
builtin_derive_macro::find_builtin_derive,
builtin_fn_macro::find_builtin_macro,
name::{name, AsName, Name},
@@ -98,9 +98,13 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
};
(
name.as_name(),
- CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId(
- idx as u32,
- )),
+ if it.disabled {
+ CustomProcMacroExpander::disabled()
+ } else {
+ CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId(
+ idx as u32,
+ ))
+ },
)
})
.collect())
@@ -604,9 +608,6 @@ impl DefCollector<'_> {
id: ItemTreeId<item_tree::Function>,
fn_id: FunctionId,
) {
- if self.def_map.block.is_some() {
- return;
- }
let kind = def.kind.to_basedb_kind();
let (expander, kind) =
match self.proc_macros.as_ref().map(|it| it.iter().find(|(n, _)| n == &def.name)) {
@@ -1120,9 +1121,16 @@ impl DefCollector<'_> {
let mut push_resolved = |directive: &MacroDirective, 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;
// Retain unresolved macros after this round of resolution.
- macros.retain(|directive| {
+ let mut retain = |directive: &MacroDirective| {
let subns = match &directive.kind {
MacroDirectiveKind::FnLike { .. } => MacroSubNs::Bang,
MacroDirectiveKind::Attr { .. } | MacroDirectiveKind::Derive { .. } => {
@@ -1156,10 +1164,11 @@ impl DefCollector<'_> {
self.def_map.modules[directive.module_id]
.scope
.add_macro_invoc(ast_id.ast_id, call_id);
+
push_resolved(directive, call_id);
res = ReachedFixedPoint::No;
- return false;
+ return Resolved::Yes;
}
}
MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, call_site } => {
@@ -1198,7 +1207,7 @@ impl DefCollector<'_> {
push_resolved(directive, call_id);
res = ReachedFixedPoint::No;
- return false;
+ return Resolved::Yes;
}
}
MacroDirectiveKind::Attr { ast_id: file_ast_id, mod_item, attr, tree } => {
@@ -1221,7 +1230,7 @@ impl DefCollector<'_> {
}
.collect(&[*mod_item], directive.container);
res = ReachedFixedPoint::No;
- false
+ Resolved::Yes
};
if let Some(ident) = path.as_ident() {
@@ -1237,13 +1246,18 @@ impl DefCollector<'_> {
let def = match resolver_def_id(path.clone()) {
Some(def) if def.is_attribute() => def,
- _ => return true,
+ _ => return Resolved::No,
};
- if matches!(
- def,
- MacroDefId { kind: MacroDefKind::BuiltInAttr(expander, _),.. }
- if expander.is_derive()
- ) {
+
+ if let MacroDefId {
+ kind:
+ MacroDefKind::BuiltInAttr(
+ BuiltinAttrExpander::Derive | BuiltinAttrExpander::DeriveConst,
+ _,
+ ),
+ ..
+ } = def
+ {
// Resolved to `#[derive]`, we don't actually expand this attribute like
// normal (as that would just be an identity expansion with extra output)
// Instead we treat derive attributes special and apply them separately.
@@ -1316,16 +1330,6 @@ impl DefCollector<'_> {
let call_id =
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
// due to duplicating functions into macro expansions
if matches!(
@@ -1337,17 +1341,29 @@ impl DefCollector<'_> {
}
if let MacroDefKind::ProcMacro(exp, ..) = def.kind {
- if exp.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
+ // 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);
+ }
+ // 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);
}
}
@@ -1358,12 +1374,13 @@ impl DefCollector<'_> {
push_resolved(directive, call_id);
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.
macros.extend(mem::take(&mut self.unresolved_macros));
self.unresolved_macros = macros;
@@ -1673,7 +1690,11 @@ impl ModCollector<'_, '_> {
FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db);
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) {
self.def_collector.export_proc_macro(
proc_macro,
diff --git a/crates/hir-def/src/nameres/diagnostics.rs b/crates/hir-def/src/nameres/diagnostics.rs
index 0a3f7bf7ec..161b2c0599 100644
--- a/crates/hir-def/src/nameres/diagnostics.rs
+++ b/crates/hir-def/src/nameres/diagnostics.rs
@@ -103,6 +103,9 @@ impl DefDiagnostic {
}
// 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(
container: LocalModuleId,
ast: MacroCallKind,
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index fd028182fa..42a8864c6a 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -129,6 +129,8 @@ pub type ExpandResult<T> = ValueResult<T, ExpandError>;
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ExpandError {
UnresolvedProcMacro(CrateId),
+ /// The macro expansion is disabled.
+ MacroDisabled,
Mbe(mbe::ExpandError),
RecursionOverflowPoisoned,
Other(Box<Box<str>>),
@@ -160,6 +162,7 @@ impl fmt::Display for ExpandError {
f.write_str(it)
}
ExpandError::Other(it) => f.write_str(it),
+ ExpandError::MacroDisabled => f.write_str("macro disabled"),
}
}
}
diff --git a/crates/hir-expand/src/proc_macro.rs b/crates/hir-expand/src/proc_macro.rs
index 70b47fc54b..7c4de2f1b8 100644
--- a/crates/hir-expand/src/proc_macro.rs
+++ b/crates/hir-expand/src/proc_macro.rs
@@ -49,6 +49,7 @@ pub struct ProcMacro {
pub name: SmolStr,
pub kind: ProcMacroKind,
pub expander: sync::Arc<dyn ProcMacroExpander>,
+ pub disabled: bool,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
@@ -56,20 +57,35 @@ pub struct CustomProcMacroExpander {
proc_macro_id: ProcMacroId,
}
-const DUMMY_ID: u32 = !0;
-
impl CustomProcMacroExpander {
+ const DUMMY_ID: u32 = !0;
+ const DISABLED_ID: u32 = !1;
+
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 }
}
- pub fn dummy() -> Self {
- Self { proc_macro_id: ProcMacroId(DUMMY_ID) }
+ /// A dummy expander that always errors. This is used for proc-macros that are missing, usually
+ /// due to them not being built yet.
+ pub const fn dummy() -> Self {
+ Self { proc_macro_id: ProcMacroId(Self::DUMMY_ID) }
}
- pub fn is_dummy(&self) -> bool {
- self.proc_macro_id.0 == DUMMY_ID
+ /// The macro was not yet resolved.
+ pub const fn is_dummy(&self) -> bool {
+ self.proc_macro_id.0 == Self::DUMMY_ID
+ }
+
+ /// A dummy expander that always errors. This expander is used for macros that have been disabled.
+ pub const fn disabled() -> Self {
+ Self { proc_macro_id: ProcMacroId(Self::DISABLED_ID) }
+ }
+
+ /// The macro is explicitly disabled and cannot be expanded.
+ pub const fn is_disabled(&self) -> bool {
+ self.proc_macro_id.0 == Self::DISABLED_ID
}
pub fn expand(
@@ -84,10 +100,14 @@ impl CustomProcMacroExpander {
mixed_site: Span,
) -> ExpandResult<tt::Subtree> {
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 }),
ExpandError::UnresolvedProcMacro(def_crate),
),
+ ProcMacroId(Self::DISABLED_ID) => ExpandResult::new(
+ tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
+ ExpandError::MacroDisabled,
+ ),
ProcMacroId(id) => {
let proc_macros = db.proc_macros();
let proc_macros = match proc_macros.get(&def_crate) {
@@ -110,7 +130,7 @@ impl CustomProcMacroExpander {
);
return ExpandResult::new(
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"),
);
}
};
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs
index c6dc071c39..a52cc19650 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -18,7 +18,6 @@ use itertools::Itertools;
use proc_macro_api::{MacroDylib, ProcMacroServer};
use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace};
use span::Span;
-use tt::DelimSpan;
use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf, VfsPath};
pub struct LoadCargoConfig {
@@ -273,7 +272,7 @@ impl SourceRootConfig {
pub fn load_proc_macro(
server: &ProcMacroServer,
path: &AbsPath,
- dummy_replace: &[Box<str>],
+ ignored_macros: &[Box<str>],
) -> ProcMacroLoadResult {
let res: Result<Vec<_>, String> = (|| {
let dylib = MacroDylib::new(path.to_path_buf());
@@ -283,7 +282,7 @@ pub fn load_proc_macro(
}
Ok(vec
.into_iter()
- .map(|expander| expander_to_proc_macro(expander, dummy_replace))
+ .map(|expander| expander_to_proc_macro(expander, ignored_macros))
.collect())
})();
match res {
@@ -349,7 +348,7 @@ fn load_crate_graph(
fn expander_to_proc_macro(
expander: proc_macro_api::ProcMacro,
- dummy_replace: &[Box<str>],
+ ignored_macros: &[Box<str>],
) -> ProcMacro {
let name = From::from(expander.name());
let kind = match expander.kind() {
@@ -357,16 +356,8 @@ fn expander_to_proc_macro(
proc_macro_api::ProcMacroKind::FuncLike => ProcMacroKind::FuncLike,
proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr,
};
- let expander: sync::Arc<dyn ProcMacroExpander> =
- if dummy_replace.iter().any(|replace| **replace == name) {
- match kind {
- ProcMacroKind::Attr => sync::Arc::new(IdentityExpander),
- _ => sync::Arc::new(EmptyExpander),
- }
- } else {
- sync::Arc::new(Expander(expander))
- };
- ProcMacro { name, kind, expander }
+ let disabled = ignored_macros.iter().any(|replace| **replace == name);
+ ProcMacro { name, kind, expander: sync::Arc::new(Expander(expander)), disabled }
}
#[derive(Debug)]
@@ -391,42 +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())
- }
-}
-
-/// 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 }))
- }
-}
-
#[cfg(test)]
mod tests {
use ide_db::base_db::SourceDatabase;
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 7bdd9ec866..bf3e71a6bd 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -1202,7 +1202,7 @@ impl Config {
Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(path)))
}
- pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
+ pub fn ignored_proc_macros(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
&self.data.procMacro_ignored
}
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 5a5d26e0b0..3c2ba2f115 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -299,13 +299,13 @@ impl GlobalState {
pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec<ProcMacroPaths>) {
tracing::info!(%cause, "will load proc macros");
- let dummy_replacements = self.config.dummy_replacements().clone();
+ let ignored_proc_macros = self.config.ignored_proc_macros().clone();
let proc_macro_clients = self.proc_macro_clients.clone();
self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap();
- let dummy_replacements = &dummy_replacements;
+ let ignored_proc_macros = &ignored_proc_macros;
let progress = {
let sender = sender.clone();
&move |msg| {
@@ -333,7 +333,12 @@ impl GlobalState {
crate_name
.as_deref()
.and_then(|crate_name| {
- dummy_replacements.get(crate_name).map(|v| &**v)
+ ignored_proc_macros.iter().find_map(
+ |(name, macros)| {
+ eq_ignore_underscore(name, crate_name)
+ .then_some(&**macros)
+ },
+ )
})
.unwrap_or_default(),
)
@@ -695,3 +700,18 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind)
}
false
}
+
+/// Similar to [`str::eq_ignore_ascii_case`] but instead of ignoring
+/// case, we say that `-` and `_` are equal.
+fn eq_ignore_underscore(s1: &str, s2: &str) -> bool {
+ if s1.len() != s2.len() {
+ return false;
+ }
+
+ s1.as_bytes().iter().zip(s2.as_bytes()).all(|(c1, c2)| {
+ let c1_underscore = c1 == &b'_' || c1 == &b'-';
+ let c2_underscore = c2 == &b'_' || c2 == &b'-';
+
+ c1 == c2 || (c1_underscore && c2_underscore)
+ })
+}
diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs
index 28e757e81b..f966013b97 100644
--- a/crates/test-fixture/src/lib.rs
+++ b/crates/test-fixture/src/lib.rs
@@ -374,6 +374,7 @@ pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
name: "identity".into(),
kind: ProcMacroKind::Attr,
expander: sync::Arc::new(IdentityProcMacroExpander),
+ disabled: false,
},
),
(
@@ -388,6 +389,7 @@ pub fn derive_identity(item: TokenStream) -> TokenStream {
name: "DeriveIdentity".into(),
kind: ProcMacroKind::CustomDerive,
expander: sync::Arc::new(IdentityProcMacroExpander),
+ disabled: false,
},
),
(
@@ -402,6 +404,7 @@ pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream {
name: "input_replace".into(),
kind: ProcMacroKind::Attr,
expander: sync::Arc::new(AttributeInputReplaceProcMacroExpander),
+ disabled: false,
},
),
(
@@ -416,6 +419,7 @@ pub fn mirror(input: TokenStream) -> TokenStream {
name: "mirror".into(),
kind: ProcMacroKind::FuncLike,
expander: sync::Arc::new(MirrorProcMacroExpander),
+ disabled: false,
},
),
(
@@ -430,6 +434,7 @@ pub fn shorten(input: TokenStream) -> TokenStream {
name: "shorten".into(),
kind: ProcMacroKind::FuncLike,
expander: sync::Arc::new(ShortenProcMacroExpander),
+ disabled: false,
},
),
]