diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index de19c10bc57c8..fd5ccc88015cc 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -32,6 +32,7 @@ use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateS use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::validate_crate_name; use rustc_session::search_paths::PathKind; +use rustc_span::def_id::DefId; use rustc_span::edition::Edition; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_target::spec::{PanicStrategy, Target, TargetTuple}; @@ -276,6 +277,12 @@ impl CStore { .filter_map(|(cnum, data)| data.as_deref().map(|data| (cnum, data))) } + pub fn all_proc_macro_def_ids(&self) -> Vec { + self.iter_crate_data() + .flat_map(|(krate, data)| data.proc_macros_for_crate(krate, self)) + .collect() + } + fn iter_crate_data_mut(&mut self) -> impl Iterator { self.metas .iter_enumerated_mut() diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 1953eef81700b..875593159c685 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1949,6 +1949,16 @@ impl CrateMetadata { self.root.is_proc_macro_crate() } + pub(crate) fn proc_macros_for_crate(&self, krate: CrateNum, cstore: &CStore) -> Vec { + let Some(data) = self.root.proc_macro_data.as_ref() else { + return vec![]; + }; + data.macros + .decode(CrateMetadataRef { cdata: self, cstore }) + .map(|index| DefId { index, krate }) + .collect() + } + pub(crate) fn name(&self) -> Symbol { self.root.header.name } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 650a827ba5644..33795ece0a441 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -169,6 +169,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } + /// Add every proc macro accessible from the current crate to the `macro_map` so diagnostics can + /// find them for suggestions. + pub(crate) fn register_macros_for_all_crates(&mut self) { + let def_ids = self.cstore().all_proc_macro_def_ids(); + for def_id in def_ids { + self.get_macro_by_def_id(def_id); + } + } + pub(crate) fn get_macro_by_def_id(&mut self, def_id: DefId) -> &MacroData { if self.macro_map.contains_key(&def_id) { return &self.macro_map[&def_id]; diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 9149974a61774..3ef55fdbaea6a 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1485,32 +1485,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { krate: &Crate, sugg_span: Option, ) { - // Bring imported but unused `derive` macros into `macro_map` so we ensure they can be used - // for suggestions. - self.visit_scopes( - ScopeSet::Macro(MacroKind::Derive), - &parent_scope, - ident.span.ctxt(), - |this, scope, _use_prelude, _ctxt| { - let Scope::Module(m, _) = scope else { - return None; - }; - for (_, resolution) in this.resolutions(m).borrow().iter() { - let Some(binding) = resolution.borrow().binding else { - continue; - }; - let Res::Def(DefKind::Macro(MacroKind::Derive | MacroKind::Attr), def_id) = - binding.res() - else { - continue; - }; - // By doing this all *imported* macros get added to the `macro_map` even if they - // are *unused*, which makes the later suggestions find them and work. - let _ = this.get_macro_by_def_id(def_id); - } - None::<()> - }, - ); + // Bring all unused `derive` macros into `macro_map` so we ensure they can be used for + // suggestions. + self.register_macros_for_all_crates(); let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind); let suggestion = self.early_lookup_typo_candidate( diff --git a/tests/ui/macros/missing-derive-3.stderr b/tests/ui/macros/missing-derive-3.stderr index 0a7ed8d08763c..9ece0d3ba3978 100644 --- a/tests/ui/macros/missing-derive-3.stderr +++ b/tests/ui/macros/missing-derive-3.stderr @@ -3,6 +3,11 @@ error: cannot find attribute `sede` in this scope | LL | #[sede(untagged)] | ^^^^ + | +help: the derive macros `Deserialize` and `Serialize` accept the similarly named `serde` attribute + | +LL | #[serde(untagged)] + | + error: cannot find attribute `serde` in this scope --> $DIR/missing-derive-3.rs:14:7 @@ -15,6 +20,11 @@ note: `serde` is imported here, but it is a crate, not an attribute | LL | extern crate serde; | ^^^^^^^^^^^^^^^^^^^ +help: `serde` is an attribute that can be used by the derive macros `Deserialize` and `Serialize`, you might be missing a `derive` attribute + | +LL + #[derive(Deserialize, Serialize)] +LL | enum B { + | error: cannot find attribute `serde` in this scope --> $DIR/missing-derive-3.rs:6:3 @@ -27,6 +37,11 @@ note: `serde` is imported here, but it is a crate, not an attribute | LL | extern crate serde; | ^^^^^^^^^^^^^^^^^^^ +help: `serde` is an attribute that can be used by the derive macros `Deserialize` and `Serialize`, you might be missing a `derive` attribute + | +LL + #[derive(Deserialize, Serialize)] +LL | enum A { + | error: aborting due to 3 previous errors diff --git a/tests/ui/proc-macro/derive-helper-legacy-spurious.stderr b/tests/ui/proc-macro/derive-helper-legacy-spurious.stderr index b34713b8ca68e..5231d6997c53b 100644 --- a/tests/ui/proc-macro/derive-helper-legacy-spurious.stderr +++ b/tests/ui/proc-macro/derive-helper-legacy-spurious.stderr @@ -9,6 +9,12 @@ error: cannot find attribute `empty_helper` in this scope | LL | #[empty_helper] | ^^^^^^^^^^^^ + | +help: `empty_helper` is an attribute that can be used by the derive macro `Empty`, you might be missing a `derive` attribute + | +LL + #[derive(Empty)] +LL | struct Foo {} + | error: aborting due to 2 previous errors