Skip to content

Commit 8baab4c

Browse files
committed
Detect missing derive on unresolved attribute even when not imported
``` 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 { | ```
1 parent c018ae5 commit 8baab4c

File tree

7 files changed

+60
-27
lines changed

7 files changed

+60
-27
lines changed

compiler/rustc_metadata/src/creader.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateS
3232
use rustc_session::lint::{self, BuiltinLintDiag};
3333
use rustc_session::output::validate_crate_name;
3434
use rustc_session::search_paths::PathKind;
35+
use rustc_span::def_id::DefId;
3536
use rustc_span::edition::Edition;
3637
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
3738
use rustc_target::spec::{PanicStrategy, Target};
@@ -275,6 +276,10 @@ impl CStore {
275276
.filter_map(|(cnum, data)| data.as_deref().map(|data| (cnum, data)))
276277
}
277278

279+
pub fn all_proc_macro_def_ids(&self) -> impl Iterator<Item = DefId> {
280+
self.iter_crate_data().flat_map(|(krate, data)| data.proc_macros_for_crate(krate, self))
281+
}
282+
278283
fn push_dependencies_in_postorder(&self, deps: &mut IndexSet<CrateNum>, cnum: CrateNum) {
279284
if !deps.contains(&cnum) {
280285
let data = self.get_crate_data(cnum);

compiler/rustc_metadata/src/rmeta/decoder.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,6 +2014,22 @@ impl CrateMetadata {
20142014
self.root.is_proc_macro_crate()
20152015
}
20162016

2017+
pub(crate) fn proc_macros_for_crate(
2018+
&self,
2019+
krate: CrateNum,
2020+
cstore: &CStore,
2021+
) -> impl Iterator<Item = DefId> {
2022+
gen move {
2023+
for def_id in self.root.proc_macro_data.as_ref().into_iter().flat_map(move |data| {
2024+
data.macros
2025+
.decode(CrateMetadataRef { cdata: self, cstore })
2026+
.map(move |index| DefId { index, krate })
2027+
}) {
2028+
yield def_id;
2029+
}
2030+
}
2031+
}
2032+
20172033
pub(crate) fn name(&self) -> Symbol {
20182034
self.root.header.name
20192035
}

compiler/rustc_resolve/src/build_reduced_graph.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
210210
}
211211
}
212212

213+
/// Add every proc macro accessible from the current crate to the `macro_map` so diagnostics can
214+
/// find them for suggestions.
215+
pub(crate) fn register_macros_for_all_crates(&mut self) {
216+
if !self.all_crate_macros_already_registered {
217+
for def_id in self.cstore().all_proc_macro_def_ids() {
218+
self.get_macro_by_def_id(def_id);
219+
}
220+
self.all_crate_macros_already_registered = true;
221+
}
222+
}
223+
213224
pub(crate) fn build_reduced_graph(
214225
&mut self,
215226
fragment: &AstFragment,

compiler/rustc_resolve/src/diagnostics.rs

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,33 +1469,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
14691469
krate: &Crate,
14701470
sugg_span: Option<Span>,
14711471
) {
1472-
// Bring imported but unused `derive` macros into `macro_map` so we ensure they can be used
1473-
// for suggestions.
1474-
self.cm().visit_scopes(
1475-
ScopeSet::Macro(MacroKind::Derive),
1476-
&parent_scope,
1477-
ident.span.ctxt(),
1478-
|this, scope, _use_prelude, _ctxt| {
1479-
let Scope::Module(m, _) = scope else {
1480-
return None;
1481-
};
1482-
for (_, resolution) in this.resolutions(m).borrow().iter() {
1483-
let Some(binding) = resolution.borrow().best_binding() else {
1484-
continue;
1485-
};
1486-
let Res::Def(DefKind::Macro(kinds), def_id) = binding.res() else {
1487-
continue;
1488-
};
1489-
if !kinds.intersects(MacroKinds::ATTR | MacroKinds::DERIVE) {
1490-
continue;
1491-
}
1492-
// By doing this all *imported* macros get added to the `macro_map` even if they
1493-
// are *unused*, which makes the later suggestions find them and work.
1494-
let _ = this.get_macro_by_def_id(def_id);
1495-
}
1496-
None::<()>
1497-
},
1498-
);
1472+
// Bring all unused `derive` macros into `macro_map` so we ensure they can be used for
1473+
// suggestions.
1474+
self.register_macros_for_all_crates();
14991475

15001476
let is_expected =
15011477
&|res: Res| res.macro_kinds().is_some_and(|k| k.contains(macro_kind.into()));

compiler/rustc_resolve/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,10 @@ pub struct Resolver<'ra, 'tcx> {
12671267

12681268
mods_with_parse_errors: FxHashSet<DefId>,
12691269

1270+
/// Whether `Resolver::register_macros_for_all_crates` has been called once already, as we
1271+
/// don't need to run it more than once.
1272+
all_crate_macros_already_registered: bool = false,
1273+
12701274
// Stores pre-expansion and pre-placeholder-fragment-insertion names for `impl Trait` types
12711275
// that were encountered during resolution. These names are used to generate item names
12721276
// for APITs, so we don't want to leak details of resolution into these names.

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

0 commit comments

Comments
 (0)