diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index e65c7a684260d..c1e688a1f9b9f 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -30,6 +30,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}; @@ -274,6 +275,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 push_dependencies_in_postorder(&self, deps: &mut IndexSet, cnum: CrateNum) { if !deps.contains(&cnum) { let data = self.get_crate_data(cnum); diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index e6aedc61338bd..eb7686b8aa30f 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -2010,6 +2010,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 ef122deba4e7b..318bb30efbee5 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -188,6 +188,19 @@ 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) { + if self.all_crate_macros_already_registered { + return; + } + self.all_crate_macros_already_registered = true; + 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 build_reduced_graph( &mut self, fragment: &AstFragment, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index c4ff5770e766e..8768470e6db1a 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1483,32 +1483,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/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 6e4c5ef1909d3..995dc9e72e60a 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -14,6 +14,7 @@ #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] +#![feature(default_field_values)] #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(rustc_attrs)] @@ -1036,7 +1037,7 @@ pub struct Resolver<'ra, 'tcx> { graph_root: Module<'ra>, - prelude: Option>, + prelude: Option> = None, extern_prelude: FxIndexMap>, /// N.B., this is used only for better diagnostics, not name resolution itself. @@ -1047,10 +1048,10 @@ pub struct Resolver<'ra, 'tcx> { field_visibility_spans: FxHashMap>, /// All imports known to succeed or fail. - determined_imports: Vec>, + determined_imports: Vec> = Vec::new(), /// All non-determined imports. - indeterminate_imports: Vec>, + indeterminate_imports: Vec> = Vec::new(), // Spans for local variables found during pattern resolution. // Used for suggestions during error reporting. @@ -1096,23 +1097,23 @@ pub struct Resolver<'ra, 'tcx> { module_map: FxIndexMap>, binding_parent_modules: FxHashMap, Module<'ra>>, - underscore_disambiguator: u32, + underscore_disambiguator: u32 = 0, /// Maps glob imports to the names of items actually imported. glob_map: FxIndexMap>, - glob_error: Option, - visibilities_for_hashing: Vec<(LocalDefId, ty::Visibility)>, + glob_error: Option = None, + visibilities_for_hashing: Vec<(LocalDefId, ty::Visibility)> = Vec::new(), used_imports: FxHashSet, maybe_unused_trait_imports: FxIndexSet, /// Privacy errors are delayed until the end in order to deduplicate them. - privacy_errors: Vec>, + privacy_errors: Vec> = Vec::new(), /// Ambiguity errors are delayed for deduplication. - ambiguity_errors: Vec>, + ambiguity_errors: Vec> = Vec::new(), /// `use` injections are delayed for better placement and deduplication. - use_injections: Vec>, + use_injections: Vec> = Vec::new(), /// Crate-local macro expanded `macro_export` referred to by a module-relative path. - macro_expanded_macro_export_errors: BTreeSet<(Span, Span)>, + macro_expanded_macro_export_errors: BTreeSet<(Span, Span)> = BTreeSet::new(), arenas: &'ra ResolverArenas<'ra>, dummy_binding: NameBinding<'ra>, @@ -1142,10 +1143,11 @@ pub struct Resolver<'ra, 'tcx> { proc_macro_stubs: FxHashSet, /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: - Vec<(Ident, MacroKind, ParentScope<'ra>, Option>, Option)>, + Vec<(Ident, MacroKind, ParentScope<'ra>, Option>, Option)> + = Vec::new(), multi_segment_macro_resolutions: - Vec<(Vec, Span, MacroKind, ParentScope<'ra>, Option, Namespace)>, - builtin_attrs: Vec<(Ident, ParentScope<'ra>)>, + Vec<(Vec, Span, MacroKind, ParentScope<'ra>, Option, Namespace)> = Vec::new(), + builtin_attrs: Vec<(Ident, ParentScope<'ra>)> = Vec::new(), /// `derive(Copy)` marks items they are applied to so they are treated specially later. /// Derive macros cannot modify the item themselves and have to store the markers in the global /// context, so they attach the markers to derive container IDs using this resolver table. @@ -1167,9 +1169,9 @@ pub struct Resolver<'ra, 'tcx> { /// Avoid duplicated errors for "name already defined". name_already_seen: FxHashMap, - potentially_unused_imports: Vec>, + potentially_unused_imports: Vec> = Vec::new(), - potentially_unnecessary_qualifications: Vec>, + potentially_unnecessary_qualifications: Vec> = Vec::new(), /// Table for mapping struct IDs into struct constructor IDs, /// it's not used during normal resolution, only for better error reporting. @@ -1178,7 +1180,7 @@ pub struct Resolver<'ra, 'tcx> { lint_buffer: LintBuffer, - next_node_id: NodeId, + next_node_id: NodeId = CRATE_NODE_ID, node_id_to_def_id: NodeMap>, @@ -1196,17 +1198,17 @@ pub struct Resolver<'ra, 'tcx> { item_generics_num_lifetimes: FxHashMap, delegation_fn_sigs: LocalDefIdMap, - main_def: Option, + main_def: Option = None, trait_impls: FxIndexMap>, /// A list of proc macro LocalDefIds, written out in the order in which /// they are declared in the static array generated by proc_macro_harness. - proc_macros: Vec, + proc_macros: Vec = Vec::new(), confused_type_with_std_module: FxIndexMap, /// Whether lifetime elision was successful. lifetime_elision_allowed: FxHashSet, /// Names of items that were stripped out via cfg with their corresponding cfg meta item. - stripped_cfg_items: Vec>, + stripped_cfg_items: Vec> = Vec::new(), effective_visibilities: EffectiveVisibilities, doc_link_resolutions: FxIndexMap, @@ -1228,6 +1230,10 @@ pub struct Resolver<'ra, 'tcx> { mods_with_parse_errors: FxHashSet, + /// Whether `Resolver::register_macros_for_all_crates` has been called once already, as we + /// don't need to run it more than once. + all_crate_macros_already_registered: bool = false, + // Stores pre-expansion and pre-placeholder-fragment-insertion names for `impl Trait` types // that were encountered during resolution. These names are used to generate item names // for APITs, so we don't want to leak details of resolution into these names. @@ -1475,15 +1481,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // The outermost module has def ID 0; this is not reflected in the // AST. graph_root, - prelude: None, extern_prelude, field_names: Default::default(), field_visibility_spans: FxHashMap::default(), - determined_imports: Vec::new(), - indeterminate_imports: Vec::new(), - pat_span_map: Default::default(), partial_res_map: Default::default(), import_res_map: Default::default(), @@ -1494,7 +1496,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { extern_crate_map: Default::default(), module_children: Default::default(), trait_map: NodeMap::default(), - underscore_disambiguator: 0, empty_module, module_map, block_map: Default::default(), @@ -1502,16 +1503,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ast_transform_scopes: FxHashMap::default(), glob_map: Default::default(), - glob_error: None, - visibilities_for_hashing: Default::default(), used_imports: FxHashSet::default(), maybe_unused_trait_imports: Default::default(), - privacy_errors: Vec::new(), - ambiguity_errors: Vec::new(), - use_injections: Vec::new(), - macro_expanded_macro_export_errors: BTreeSet::new(), - arenas, dummy_binding: (Res::Err, pub_vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(arenas), builtin_types_bindings: PrimTy::ALL @@ -1559,27 +1553,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { derive_data: Default::default(), local_macro_def_scopes: FxHashMap::default(), name_already_seen: FxHashMap::default(), - potentially_unused_imports: Vec::new(), - potentially_unnecessary_qualifications: Default::default(), struct_constructors: Default::default(), unused_macros: Default::default(), unused_macro_rules: Default::default(), proc_macro_stubs: Default::default(), - single_segment_macro_resolutions: Default::default(), - multi_segment_macro_resolutions: Default::default(), - builtin_attrs: Default::default(), containers_deriving_copy: Default::default(), lint_buffer: LintBuffer::default(), - next_node_id: CRATE_NODE_ID, node_id_to_def_id, disambiguator: DisambiguatorState::new(), placeholder_field_indices: Default::default(), invocation_parents, legacy_const_generic_args: Default::default(), item_generics_num_lifetimes: Default::default(), - main_def: Default::default(), trait_impls: Default::default(), - proc_macros: Default::default(), confused_type_with_std_module: Default::default(), lifetime_elision_allowed: Default::default(), stripped_cfg_items: Default::default(), @@ -1594,6 +1580,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { current_crate_outer_attr_insert_span, mods_with_parse_errors: Default::default(), impl_trait_names: Default::default(), + .. }; let root_parent_scope = ParentScope::module(graph_root, &resolver); diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 001699b2bc726..396abb001cee6 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -559,6 +559,12 @@ error: cannot find attribute `error` in this scope | LL | #[error(no_crate_example, code = E0123)] | ^^^^^ + | +help: `error` is an attribute that can be used by the derive macro `Error`, you might be missing a `derive` attribute + | +LL + #[derive(Error)] +LL | struct ErrorAttribute {} + | error: cannot find attribute `warn_` in this scope --> $DIR/diagnostic-derive.rs:590:3 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