Skip to content

Commit 07b2f21

Browse files
Auto merge of #142487 - estebank:serde-attr-5, r=<try>
Detect missing `derive` on unresolved attribute even when not imported When encountering unresolved attributes, ensure the proc-macros for every crate in scope are added to the `macro_map` so that typos and missing `derive`s are properly detected. ``` error: cannot find attribute `sede` in this scope --> $DIR/missing-derive-3.rs:20:7 | 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 | LL | #[serde(untagged)] | ^^^^^ | note: `serde` is imported here, but it is a crate, not an attribute --> $DIR/missing-derive-3.rs:4:1 | 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 { | ``` Follow up to #134841.
2 parents 855e0fe + d617a5b commit 07b2f21

File tree

8 files changed

+86
-65
lines changed

8 files changed

+86
-65
lines changed

compiler/rustc_metadata/src/creader.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateS
3030
use rustc_session::lint::{self, BuiltinLintDiag};
3131
use rustc_session::output::validate_crate_name;
3232
use rustc_session::search_paths::PathKind;
33+
use rustc_span::def_id::DefId;
3334
use rustc_span::edition::Edition;
3435
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
3536
use rustc_target::spec::{PanicStrategy, Target};
@@ -274,6 +275,12 @@ impl CStore {
274275
.filter_map(|(cnum, data)| data.as_deref().map(|data| (cnum, data)))
275276
}
276277

278+
pub fn all_proc_macro_def_ids(&self) -> Vec<DefId> {
279+
self.iter_crate_data()
280+
.flat_map(|(krate, data)| data.proc_macros_for_crate(krate, self))
281+
.collect()
282+
}
283+
277284
fn push_dependencies_in_postorder(&self, deps: &mut IndexSet<CrateNum>, cnum: CrateNum) {
278285
if !deps.contains(&cnum) {
279286
let data = self.get_crate_data(cnum);

compiler/rustc_metadata/src/rmeta/decoder.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,6 +2010,16 @@ impl CrateMetadata {
20102010
self.root.is_proc_macro_crate()
20112011
}
20122012

2013+
pub(crate) fn proc_macros_for_crate(&self, krate: CrateNum, cstore: &CStore) -> Vec<DefId> {
2014+
let Some(data) = self.root.proc_macro_data.as_ref() else {
2015+
return vec![];
2016+
};
2017+
data.macros
2018+
.decode(CrateMetadataRef { cdata: self, cstore })
2019+
.map(|index| DefId { index, krate })
2020+
.collect()
2021+
}
2022+
20132023
pub(crate) fn name(&self) -> Symbol {
20142024
self.root.header.name
20152025
}

compiler/rustc_resolve/src/build_reduced_graph.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
188188
}
189189
}
190190

191+
/// Add every proc macro accessible from the current crate to the `macro_map` so diagnostics can
192+
/// find them for suggestions.
193+
pub(crate) fn register_macros_for_all_crates(&mut self) {
194+
if self.all_crate_macros_already_registered {
195+
return;
196+
}
197+
self.all_crate_macros_already_registered = true;
198+
let def_ids = self.cstore().all_proc_macro_def_ids();
199+
for def_id in def_ids {
200+
self.get_macro_by_def_id(def_id);
201+
}
202+
}
203+
191204
pub(crate) fn build_reduced_graph(
192205
&mut self,
193206
fragment: &AstFragment,

compiler/rustc_resolve/src/diagnostics.rs

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,32 +1483,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
14831483
krate: &Crate,
14841484
sugg_span: Option<Span>,
14851485
) {
1486-
// Bring imported but unused `derive` macros into `macro_map` so we ensure they can be used
1487-
// for suggestions.
1488-
self.visit_scopes(
1489-
ScopeSet::Macro(MacroKind::Derive),
1490-
&parent_scope,
1491-
ident.span.ctxt(),
1492-
|this, scope, _use_prelude, _ctxt| {
1493-
let Scope::Module(m, _) = scope else {
1494-
return None;
1495-
};
1496-
for (_, resolution) in this.resolutions(m).borrow().iter() {
1497-
let Some(binding) = resolution.borrow().binding else {
1498-
continue;
1499-
};
1500-
let Res::Def(DefKind::Macro(MacroKind::Derive | MacroKind::Attr), def_id) =
1501-
binding.res()
1502-
else {
1503-
continue;
1504-
};
1505-
// By doing this all *imported* macros get added to the `macro_map` even if they
1506-
// are *unused*, which makes the later suggestions find them and work.
1507-
let _ = this.get_macro_by_def_id(def_id);
1508-
}
1509-
None::<()>
1510-
},
1511-
);
1486+
// Bring all unused `derive` macros into `macro_map` so we ensure they can be used for
1487+
// suggestions.
1488+
self.register_macros_for_all_crates();
15121489

15131490
let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind);
15141491
let suggestion = self.early_lookup_typo_candidate(

compiler/rustc_resolve/src/lib.rs

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#![doc(rust_logo)]
1515
#![feature(assert_matches)]
1616
#![feature(box_patterns)]
17+
#![feature(default_field_values)]
1718
#![feature(if_let_guard)]
1819
#![feature(iter_intersperse)]
1920
#![feature(rustc_attrs)]
@@ -1036,7 +1037,7 @@ pub struct Resolver<'ra, 'tcx> {
10361037

10371038
graph_root: Module<'ra>,
10381039

1039-
prelude: Option<Module<'ra>>,
1040+
prelude: Option<Module<'ra>> = None,
10401041
extern_prelude: FxIndexMap<Ident, ExternPreludeEntry<'ra>>,
10411042

10421043
/// N.B., this is used only for better diagnostics, not name resolution itself.
@@ -1047,10 +1048,10 @@ pub struct Resolver<'ra, 'tcx> {
10471048
field_visibility_spans: FxHashMap<DefId, Vec<Span>>,
10481049

10491050
/// All imports known to succeed or fail.
1050-
determined_imports: Vec<Import<'ra>>,
1051+
determined_imports: Vec<Import<'ra>> = Vec::new(),
10511052

10521053
/// All non-determined imports.
1053-
indeterminate_imports: Vec<Import<'ra>>,
1054+
indeterminate_imports: Vec<Import<'ra>> = Vec::new(),
10541055

10551056
// Spans for local variables found during pattern resolution.
10561057
// Used for suggestions during error reporting.
@@ -1096,23 +1097,23 @@ pub struct Resolver<'ra, 'tcx> {
10961097
module_map: FxIndexMap<DefId, Module<'ra>>,
10971098
binding_parent_modules: FxHashMap<NameBinding<'ra>, Module<'ra>>,
10981099

1099-
underscore_disambiguator: u32,
1100+
underscore_disambiguator: u32 = 0,
11001101

11011102
/// Maps glob imports to the names of items actually imported.
11021103
glob_map: FxIndexMap<LocalDefId, FxIndexSet<Symbol>>,
1103-
glob_error: Option<ErrorGuaranteed>,
1104-
visibilities_for_hashing: Vec<(LocalDefId, ty::Visibility)>,
1104+
glob_error: Option<ErrorGuaranteed> = None,
1105+
visibilities_for_hashing: Vec<(LocalDefId, ty::Visibility)> = Vec::new(),
11051106
used_imports: FxHashSet<NodeId>,
11061107
maybe_unused_trait_imports: FxIndexSet<LocalDefId>,
11071108

11081109
/// Privacy errors are delayed until the end in order to deduplicate them.
1109-
privacy_errors: Vec<PrivacyError<'ra>>,
1110+
privacy_errors: Vec<PrivacyError<'ra>> = Vec::new(),
11101111
/// Ambiguity errors are delayed for deduplication.
1111-
ambiguity_errors: Vec<AmbiguityError<'ra>>,
1112+
ambiguity_errors: Vec<AmbiguityError<'ra>> = Vec::new(),
11121113
/// `use` injections are delayed for better placement and deduplication.
1113-
use_injections: Vec<UseError<'tcx>>,
1114+
use_injections: Vec<UseError<'tcx>> = Vec::new(),
11141115
/// Crate-local macro expanded `macro_export` referred to by a module-relative path.
1115-
macro_expanded_macro_export_errors: BTreeSet<(Span, Span)>,
1116+
macro_expanded_macro_export_errors: BTreeSet<(Span, Span)> = BTreeSet::new(),
11161117

11171118
arenas: &'ra ResolverArenas<'ra>,
11181119
dummy_binding: NameBinding<'ra>,
@@ -1142,10 +1143,11 @@ pub struct Resolver<'ra, 'tcx> {
11421143
proc_macro_stubs: FxHashSet<LocalDefId>,
11431144
/// Traces collected during macro resolution and validated when it's complete.
11441145
single_segment_macro_resolutions:
1145-
Vec<(Ident, MacroKind, ParentScope<'ra>, Option<NameBinding<'ra>>, Option<Span>)>,
1146+
Vec<(Ident, MacroKind, ParentScope<'ra>, Option<NameBinding<'ra>>, Option<Span>)>
1147+
= Vec::new(),
11461148
multi_segment_macro_resolutions:
1147-
Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'ra>, Option<Res>, Namespace)>,
1148-
builtin_attrs: Vec<(Ident, ParentScope<'ra>)>,
1149+
Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'ra>, Option<Res>, Namespace)> = Vec::new(),
1150+
builtin_attrs: Vec<(Ident, ParentScope<'ra>)> = Vec::new(),
11491151
/// `derive(Copy)` marks items they are applied to so they are treated specially later.
11501152
/// Derive macros cannot modify the item themselves and have to store the markers in the global
11511153
/// context, so they attach the markers to derive container IDs using this resolver table.
@@ -1167,9 +1169,9 @@ pub struct Resolver<'ra, 'tcx> {
11671169
/// Avoid duplicated errors for "name already defined".
11681170
name_already_seen: FxHashMap<Symbol, Span>,
11691171

1170-
potentially_unused_imports: Vec<Import<'ra>>,
1172+
potentially_unused_imports: Vec<Import<'ra>> = Vec::new(),
11711173

1172-
potentially_unnecessary_qualifications: Vec<UnnecessaryQualification<'ra>>,
1174+
potentially_unnecessary_qualifications: Vec<UnnecessaryQualification<'ra>> = Vec::new(),
11731175

11741176
/// Table for mapping struct IDs into struct constructor IDs,
11751177
/// it's not used during normal resolution, only for better error reporting.
@@ -1178,7 +1180,7 @@ pub struct Resolver<'ra, 'tcx> {
11781180

11791181
lint_buffer: LintBuffer,
11801182

1181-
next_node_id: NodeId,
1183+
next_node_id: NodeId = CRATE_NODE_ID,
11821184

11831185
node_id_to_def_id: NodeMap<Feed<'tcx, LocalDefId>>,
11841186

@@ -1196,17 +1198,17 @@ pub struct Resolver<'ra, 'tcx> {
11961198
item_generics_num_lifetimes: FxHashMap<LocalDefId, usize>,
11971199
delegation_fn_sigs: LocalDefIdMap<DelegationFnSig>,
11981200

1199-
main_def: Option<MainDefinition>,
1201+
main_def: Option<MainDefinition> = None,
12001202
trait_impls: FxIndexMap<DefId, Vec<LocalDefId>>,
12011203
/// A list of proc macro LocalDefIds, written out in the order in which
12021204
/// they are declared in the static array generated by proc_macro_harness.
1203-
proc_macros: Vec<LocalDefId>,
1205+
proc_macros: Vec<LocalDefId> = Vec::new(),
12041206
confused_type_with_std_module: FxIndexMap<Span, Span>,
12051207
/// Whether lifetime elision was successful.
12061208
lifetime_elision_allowed: FxHashSet<NodeId>,
12071209

12081210
/// Names of items that were stripped out via cfg with their corresponding cfg meta item.
1209-
stripped_cfg_items: Vec<StrippedCfgItem<NodeId>>,
1211+
stripped_cfg_items: Vec<StrippedCfgItem<NodeId>> = Vec::new(),
12101212

12111213
effective_visibilities: EffectiveVisibilities,
12121214
doc_link_resolutions: FxIndexMap<LocalDefId, DocLinkResMap>,
@@ -1228,6 +1230,10 @@ pub struct Resolver<'ra, 'tcx> {
12281230

12291231
mods_with_parse_errors: FxHashSet<DefId>,
12301232

1233+
/// Whether `Resolver::register_macros_for_all_crates` has been called once already, as we
1234+
/// don't need to run it more than once.
1235+
all_crate_macros_already_registered: bool = false,
1236+
12311237
// Stores pre-expansion and pre-placeholder-fragment-insertion names for `impl Trait` types
12321238
// that were encountered during resolution. These names are used to generate item names
12331239
// 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> {
14751481
// The outermost module has def ID 0; this is not reflected in the
14761482
// AST.
14771483
graph_root,
1478-
prelude: None,
14791484
extern_prelude,
14801485

14811486
field_names: Default::default(),
14821487
field_visibility_spans: FxHashMap::default(),
14831488

1484-
determined_imports: Vec::new(),
1485-
indeterminate_imports: Vec::new(),
1486-
14871489
pat_span_map: Default::default(),
14881490
partial_res_map: Default::default(),
14891491
import_res_map: Default::default(),
@@ -1494,24 +1496,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
14941496
extern_crate_map: Default::default(),
14951497
module_children: Default::default(),
14961498
trait_map: NodeMap::default(),
1497-
underscore_disambiguator: 0,
14981499
empty_module,
14991500
module_map,
15001501
block_map: Default::default(),
15011502
binding_parent_modules: FxHashMap::default(),
15021503
ast_transform_scopes: FxHashMap::default(),
15031504

15041505
glob_map: Default::default(),
1505-
glob_error: None,
1506-
visibilities_for_hashing: Default::default(),
15071506
used_imports: FxHashSet::default(),
15081507
maybe_unused_trait_imports: Default::default(),
15091508

1510-
privacy_errors: Vec::new(),
1511-
ambiguity_errors: Vec::new(),
1512-
use_injections: Vec::new(),
1513-
macro_expanded_macro_export_errors: BTreeSet::new(),
1514-
15151509
arenas,
15161510
dummy_binding: (Res::Err, pub_vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(arenas),
15171511
builtin_types_bindings: PrimTy::ALL
@@ -1559,27 +1553,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
15591553
derive_data: Default::default(),
15601554
local_macro_def_scopes: FxHashMap::default(),
15611555
name_already_seen: FxHashMap::default(),
1562-
potentially_unused_imports: Vec::new(),
1563-
potentially_unnecessary_qualifications: Default::default(),
15641556
struct_constructors: Default::default(),
15651557
unused_macros: Default::default(),
15661558
unused_macro_rules: Default::default(),
15671559
proc_macro_stubs: Default::default(),
1568-
single_segment_macro_resolutions: Default::default(),
1569-
multi_segment_macro_resolutions: Default::default(),
1570-
builtin_attrs: Default::default(),
15711560
containers_deriving_copy: Default::default(),
15721561
lint_buffer: LintBuffer::default(),
1573-
next_node_id: CRATE_NODE_ID,
15741562
node_id_to_def_id,
15751563
disambiguator: DisambiguatorState::new(),
15761564
placeholder_field_indices: Default::default(),
15771565
invocation_parents,
15781566
legacy_const_generic_args: Default::default(),
15791567
item_generics_num_lifetimes: Default::default(),
1580-
main_def: Default::default(),
15811568
trait_impls: Default::default(),
1582-
proc_macros: Default::default(),
15831569
confused_type_with_std_module: Default::default(),
15841570
lifetime_elision_allowed: Default::default(),
15851571
stripped_cfg_items: Default::default(),
@@ -1594,6 +1580,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
15941580
current_crate_outer_attr_insert_span,
15951581
mods_with_parse_errors: Default::default(),
15961582
impl_trait_names: Default::default(),
1583+
..
15971584
};
15981585

15991586
let root_parent_scope = ParentScope::module(graph_root, &resolver);

tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,12 @@ error: cannot find attribute `error` in this scope
559559
|
560560
LL | #[error(no_crate_example, code = E0123)]
561561
| ^^^^^
562+
|
563+
help: `error` is an attribute that can be used by the derive macro `Error`, you might be missing a `derive` attribute
564+
|
565+
LL + #[derive(Error)]
566+
LL | struct ErrorAttribute {}
567+
|
562568

563569
error: cannot find attribute `warn_` in this scope
564570
--> $DIR/diagnostic-derive.rs:590:3

tests/ui/macros/missing-derive-3.stderr

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ error: cannot find attribute `sede` in this scope
33
|
44
LL | #[sede(untagged)]
55
| ^^^^
6+
|
7+
help: the derive macros `Deserialize` and `Serialize` accept the similarly named `serde` attribute
8+
|
9+
LL | #[serde(untagged)]
10+
| +
611

712
error: cannot find attribute `serde` in this scope
813
--> $DIR/missing-derive-3.rs:14:7
@@ -15,6 +20,11 @@ note: `serde` is imported here, but it is a crate, not an attribute
1520
|
1621
LL | extern crate serde;
1722
| ^^^^^^^^^^^^^^^^^^^
23+
help: `serde` is an attribute that can be used by the derive macros `Deserialize` and `Serialize`, you might be missing a `derive` attribute
24+
|
25+
LL + #[derive(Deserialize, Serialize)]
26+
LL | enum B {
27+
|
1828

1929
error: cannot find attribute `serde` in this scope
2030
--> $DIR/missing-derive-3.rs:6:3
@@ -27,6 +37,11 @@ note: `serde` is imported here, but it is a crate, not an attribute
2737
|
2838
LL | extern crate serde;
2939
| ^^^^^^^^^^^^^^^^^^^
40+
help: `serde` is an attribute that can be used by the derive macros `Deserialize` and `Serialize`, you might be missing a `derive` attribute
41+
|
42+
LL + #[derive(Deserialize, Serialize)]
43+
LL | enum A {
44+
|
3045

3146
error: aborting due to 3 previous errors
3247

tests/ui/proc-macro/derive-helper-legacy-spurious.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ error: cannot find attribute `empty_helper` in this scope
99
|
1010
LL | #[empty_helper]
1111
| ^^^^^^^^^^^^
12+
|
13+
help: `empty_helper` is an attribute that can be used by the derive macro `Empty`, you might be missing a `derive` attribute
14+
|
15+
LL + #[derive(Empty)]
16+
LL | struct Foo {}
17+
|
1218

1319
error: aborting due to 2 previous errors
1420

0 commit comments

Comments
 (0)