diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 44a3d4e628ebc..a55aac73af301 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -4,7 +4,7 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{self as ast, Crate, ItemKind, ModKind, NodeId, Path, CRATE_NODE_ID}; use rustc_ast_pretty::pprust; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{ pluralize, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; @@ -1016,6 +1016,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, false, false, + None, ) { suggestions.extend( ext.helper_attrs @@ -1331,7 +1332,30 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { macro_kind: MacroKind, parent_scope: &ParentScope<'a>, ident: Ident, + 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::<()> + }, + ); + let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind); let suggestion = self.early_lookup_typo_candidate( ScopeSet::Macro(macro_kind), @@ -1339,7 +1363,95 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ident, is_expected, ); - self.add_typo_suggestion(err, suggestion, ident.span); + if !self.add_typo_suggestion(err, suggestion, ident.span) { + // FIXME: this only works if the macro that has the helper_attr has already + // been imported. + let mut derives = vec![]; + let mut all_attrs: FxHashMap> = FxHashMap::default(); + for (def_id, data) in &self.macro_map { + for helper_attr in &data.ext.helper_attrs { + let item_name = self.tcx.item_name(*def_id); + all_attrs.entry(*helper_attr).or_default().push(item_name); + if helper_attr == &ident.name { + // FIXME: we should also do Levenshtein distance checks here. + derives.push(item_name); + } + } + } + let kind = MacroKind::Derive.descr(); + if !derives.is_empty() { + derives.sort(); + derives.dedup(); + let msg = match &derives[..] { + [derive] => format!(" `{derive}`"), + [start @ .., last] => format!( + "s {} and `{last}`", + start.iter().map(|d| format!("`{d}`")).collect::>().join(", ") + ), + [] => unreachable!("we checked for this to be non-empty 10 lines above!?"), + }; + let msg = format!( + "`{}` is an attribute that can be used by the {kind}{msg}, you might be missing a \ + `derive` attribute", + ident.name, + ); + let sugg_span = + if let ModuleKind::Def(DefKind::Enum, id, _) = parent_scope.module.kind { + let span = self.def_span(id); + if span.from_expansion() { + None + } else { + // For enum variants, `sugg_span` is empty, but we can get the `enum`'s `Span`. + Some(span.shrink_to_lo()) + } + } else { + // For items, this `Span` will be populated, everything else it'll be `None`. + sugg_span + }; + match sugg_span { + Some(span) => { + err.span_suggestion_verbose( + span, + &msg, + format!( + "#[derive({})]\n", + derives + .iter() + .map(|d| d.to_string()) + .collect::>() + .join(", ") + ), + Applicability::MaybeIncorrect, + ); + } + None => { + err.note(&msg); + } + } + } else { + let all_attr_names: Vec = all_attrs.keys().cloned().collect(); + if let Some(best_match) = find_best_match_for_name(&all_attr_names, ident.name, None) + && let Some(macros) = all_attrs.get(&best_match) + && !macros.is_empty() + { + let msg = match ¯os[..] { + [] => unreachable!("we checked above in the if-let"), + [name] => format!(" `{name}` accepts"), + [start @ .., end] => format!( + "s {} and `{end}` accept", + start.iter().map(|m| format!("`{m}`")).collect::>().join(", "), + ), + }; + let msg = format!("the {kind}{msg} the similarly named `{best_match}` attribute"); + err.span_suggestion_verbose( + ident.span, + &msg, + best_match, + Applicability::MaybeIncorrect, + ); + } + } + } let import_suggestions = self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected); @@ -1364,14 +1476,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { err.help("have you added the `#[macro_use]` on the module/import?"); return; } + if ident.name == kw::Default && let ModuleKind::Def(DefKind::Enum, def_id, _) = parent_scope.module.kind { let span = self.def_span(def_id); let source_map = self.tcx.sess.source_map(); let head_span = source_map.guess_head_span(span); - if let Ok(head) = source_map.span_to_snippet(head_span) { - err.span_suggestion(head_span, "consider adding a derive", format!("#[derive(Default)]\n{head}"), Applicability::MaybeIncorrect); + if let Ok(_) = source_map.span_to_snippet(head_span) { + err.span_suggestion( + head_span.shrink_to_lo(), + "consider adding a derive", + format!("#[derive(Default)]\n"), + Applicability::MaybeIncorrect, + ); } else { err.span_help( head_span, diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 52f0b65fad672..6f1a0c97563cc 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -459,6 +459,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, true, force, + None, ) { Ok((Some(ext), _)) => { if ext.helper_attrs.contains(&ident.name) { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4ca54bab31a68..50465cab62872 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3706,7 +3706,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let path_seg = |seg: &Segment| PathSegment::from_ident(seg.ident); let path = Path { segments: path.iter().map(path_seg).collect(), span, tokens: None }; if let Ok((_, res)) = - self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false) + self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false, None) { return Ok(Some(PartialRes::new(res))); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 0e84432a5b4bf..6b3ab8887cd95 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -974,9 +974,9 @@ pub struct Resolver<'a, 'tcx> { proc_macro_stubs: FxHashSet, /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: - Vec<(Ident, MacroKind, ParentScope<'a>, Option<&'a NameBinding<'a>>)>, + Vec<(Ident, MacroKind, ParentScope<'a>, Option<&'a NameBinding<'a>>, Option)>, multi_segment_macro_resolutions: - Vec<(Vec, Span, MacroKind, ParentScope<'a>, Option)>, + Vec<(Vec, Span, MacroKind, ParentScope<'a>, Option, Option)>, builtin_attrs: Vec<(Ident, ParentScope<'a>)>, /// `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 diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 48707d37a101c..eb7ab1639aa66 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -276,6 +276,14 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { let parent_scope = &ParentScope { derives, ..parent_scope }; let supports_macro_expansion = invoc.fragment_kind.supports_macro_expansion(); let node_id = invoc.expansion_data.lint_node_id; + let sugg_span = match &invoc.kind { + InvocationKind::Attr { item: Annotatable::Item(item), .. } + if !item.span.from_expansion() => + { + Some(item.span.shrink_to_lo()) + } + _ => None, + }; let (ext, res) = self.smart_resolve_macro_path( path, kind, @@ -285,6 +293,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { node_id, force, soft_custom_inner_attributes_gate(path, invoc), + sugg_span, )?; let span = invoc.span(); @@ -369,6 +378,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { &parent_scope, true, force, + None, ) { Ok((Some(ext), _)) => { if !ext.helper_attrs.is_empty() { @@ -485,14 +495,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { node_id: NodeId, force: bool, soft_custom_inner_attributes_gate: bool, + sugg_span: Option, ) -> Result<(Lrc, Res), Indeterminate> { - let (ext, res) = match self.resolve_macro_path(path, Some(kind), parent_scope, true, force) - { - Ok((Some(ext), res)) => (ext, res), - Ok((None, res)) => (self.dummy_ext(kind), res), - Err(Determinacy::Determined) => (self.dummy_ext(kind), Res::Err), - Err(Determinacy::Undetermined) => return Err(Indeterminate), - }; + let (ext, res) = + match self.resolve_macro_path(path, Some(kind), parent_scope, true, force, sugg_span) { + Ok((Some(ext), res)) => (ext, res), + Ok((None, res)) => (self.dummy_ext(kind), res), + Err(Determinacy::Determined) => (self.dummy_ext(kind), Res::Err), + Err(Determinacy::Undetermined) => return Err(Indeterminate), + }; // Report errors for the resolved macro. for segment in &path.segments { @@ -585,6 +596,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, trace: bool, force: bool, + sugg_span: Option, ) -> Result<(Option>, Res), Determinacy> { let path_span = path.span; let mut path = Segment::from_path(path); @@ -616,6 +628,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { kind, *parent_scope, res.ok(), + sugg_span, )); } @@ -642,6 +655,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { kind, *parent_scope, binding.ok(), + sugg_span, )); } @@ -688,7 +702,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }; let macro_resolutions = mem::take(&mut self.multi_segment_macro_resolutions); - for (mut path, path_span, kind, parent_scope, initial_res) in macro_resolutions { + for (mut path, path_span, kind, parent_scope, initial_res, _sugg_span) in macro_resolutions + { // FIXME: Path resolution will ICE if segment IDs present. for seg in &mut path { seg.id = None; @@ -713,9 +728,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let exclamation_span = sm.next_point(span); suggestion = Some(( vec![(exclamation_span, "".to_string())], - format!("{} is not a macro, but a {}, try to remove `!`", Segment::names_to_string(&path), partial_res.base_res().descr()), - Applicability::MaybeIncorrect - )); + format!( + "{} is not a macro, but a {}, try to remove `!`", + Segment::names_to_string(&path), + partial_res.base_res().descr(), + ), + Applicability::MaybeIncorrect, + )); } (span, label) } else { @@ -738,7 +757,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } let macro_resolutions = mem::take(&mut self.single_segment_macro_resolutions); - for (ident, kind, parent_scope, initial_binding) in macro_resolutions { + for (ident, kind, parent_scope, initial_binding, sugg_span) in macro_resolutions { match self.early_resolve_ident_in_lexical_scope( ident, ScopeSet::Macro(kind), @@ -771,9 +790,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } Err(..) => { let expected = kind.descr_expected(); - let msg = format!("cannot find {} `{}` in this scope", expected, ident); + let msg = format!("cannot find {expected} `{ident}` in this scope"); let mut err = self.tcx.sess.struct_span_err(ident.span, &msg); - self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident); + self.unresolved_macro_suggestions( + &mut err, + kind, + &parent_scope, + ident, + sugg_span, + ); err.emit(); } } diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 513b675e5dd41..a5834de02dc8f 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -646,18 +646,30 @@ error: cannot find attribute `multipart_suggestion` in this scope | LL | #[multipart_suggestion(no_crate_suggestion)] | ^^^^^^^^^^^^^^^^^^^^ + | +help: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute + | +LL | #[derive(Subdiagnostic)] + | error: cannot find attribute `multipart_suggestion` in this scope --> $DIR/diagnostic-derive.rs:645:3 | LL | #[multipart_suggestion()] | ^^^^^^^^^^^^^^^^^^^^ + | +help: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute + | +LL | #[derive(Subdiagnostic)] + | error: cannot find attribute `multipart_suggestion` in this scope --> $DIR/diagnostic-derive.rs:649:7 | LL | #[multipart_suggestion(no_crate_suggestion)] | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute error[E0425]: cannot find value `nonsense` in module `crate::fluent_generated` --> $DIR/diagnostic-derive.rs:70:8 diff --git a/tests/ui/enum/suggest-default-attribute.stderr b/tests/ui/enum/suggest-default-attribute.stderr index fb830d3f78b64..f1807e3bb8530 100644 --- a/tests/ui/enum/suggest-default-attribute.stderr +++ b/tests/ui/enum/suggest-default-attribute.stderr @@ -6,8 +6,7 @@ LL | #[default] | help: consider adding a derive | -LL + #[derive(Default)] -LL ~ pub enum Test { +LL | #[derive(Default)] | error: aborting due to previous error diff --git a/tests/ui/macros/auxiliary/serde.rs b/tests/ui/macros/auxiliary/serde.rs new file mode 100644 index 0000000000000..5e8c678f710d7 --- /dev/null +++ b/tests/ui/macros/auxiliary/serde.rs @@ -0,0 +1,19 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::*; + +#[proc_macro_derive(Serialize, attributes(serde))] +pub fn serialize(ts: TokenStream) -> TokenStream { + quote!{} +} + +#[proc_macro_derive(Deserialize, attributes(serde))] +pub fn deserialize(ts: TokenStream) -> TokenStream { + quote!{} +} diff --git a/tests/ui/macros/missing-derive-1.rs b/tests/ui/macros/missing-derive-1.rs new file mode 100644 index 0000000000000..59828285bf28d --- /dev/null +++ b/tests/ui/macros/missing-derive-1.rs @@ -0,0 +1,33 @@ +// aux-build:serde.rs + +// derive macros imported and used + +extern crate serde; +use serde::{Serialize, Deserialize}; + +#[serde(untagged)] //~ ERROR cannot find attribute `serde` +enum A { //~ HELP `serde` is an attribute that can be used by the derive macros `Serialize` and `Deserialize` + A, + B, +} + +enum B { //~ HELP `serde` is an attribute that can be used by the derive macros `Serialize` and `Deserialize` + A, + #[serde(untagged)] //~ ERROR cannot find attribute `serde` + B, +} + +enum C { + A, + #[sede(untagged)] //~ ERROR cannot find attribute `sede` + B, //~^ HELP the derive macros `Serialize` and `Deserialize` accept the similarly named `serde` attribute +} + +#[derive(Serialize, Deserialize)] +#[serde(untagged)] +enum D { + A, + B, +} + +fn main() {} diff --git a/tests/ui/macros/missing-derive-1.stderr b/tests/ui/macros/missing-derive-1.stderr new file mode 100644 index 0000000000000..74c31d814b8f2 --- /dev/null +++ b/tests/ui/macros/missing-derive-1.stderr @@ -0,0 +1,45 @@ +error: cannot find attribute `serde` in this scope + --> $DIR/missing-derive-1.rs:8:3 + | +LL | #[serde(untagged)] + | ^^^^^ + | +note: `serde` is imported here, but it is a crate, not an attribute + --> $DIR/missing-derive-1.rs:5:1 + | +LL | extern crate serde; + | ^^^^^^^^^^^^^^^^^^^ +help: `serde` is an attribute that can be used by the derive macros `Serialize` and `Deserialize`, you might be missing a `derive` attribute + | +LL | #[derive(Serialize, Deserialize)] + | + +error: cannot find attribute `serde` in this scope + --> $DIR/missing-derive-1.rs:16:7 + | +LL | #[serde(untagged)] + | ^^^^^ + | +note: `serde` is imported here, but it is a crate, not an attribute + --> $DIR/missing-derive-1.rs:5:1 + | +LL | extern crate serde; + | ^^^^^^^^^^^^^^^^^^^ +help: `serde` is an attribute that can be used by the derive macros `Serialize` and `Deserialize`, you might be missing a `derive` attribute + | +LL | #[derive(Serialize, Deserialize)] + | + +error: cannot find attribute `sede` in this scope + --> $DIR/missing-derive-1.rs:22:7 + | +LL | #[sede(untagged)] + | ^^^^ + | +help: the derive macros `Serialize` and `Deserialize` accept the similarly named `serde` attribute + | +LL | #[serde(untagged)] + | ~~~~~ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/macros/missing-derive-2.rs b/tests/ui/macros/missing-derive-2.rs new file mode 100644 index 0000000000000..24efa71b1be86 --- /dev/null +++ b/tests/ui/macros/missing-derive-2.rs @@ -0,0 +1,26 @@ +// aux-build:serde.rs + +// derive macros imported but unused + +extern crate serde; +use serde::{Serialize, Deserialize}; + +#[serde(untagged)] //~ ERROR cannot find attribute `serde` +enum A { //~ HELP `serde` is an attribute that can be used by the derive macros `Serialize` and `Deserialize` + A, + B, +} + +enum B { //~ HELP `serde` is an attribute that can be used by the derive macros `Serialize` and `Deserialize` + A, + #[serde(untagged)] //~ ERROR cannot find attribute `serde` + B, +} + +enum C { + A, + #[sede(untagged)] //~ ERROR cannot find attribute `sede` + B, //~^ HELP the derive macros `Serialize` and `Deserialize` accept the similarly named `serde` attribute +} + +fn main() {} diff --git a/tests/ui/macros/missing-derive-2.stderr b/tests/ui/macros/missing-derive-2.stderr new file mode 100644 index 0000000000000..2a890d8bd9e20 --- /dev/null +++ b/tests/ui/macros/missing-derive-2.stderr @@ -0,0 +1,45 @@ +error: cannot find attribute `sede` in this scope + --> $DIR/missing-derive-2.rs:22:7 + | +LL | #[sede(untagged)] + | ^^^^ + | +help: the derive macros `Serialize` and `Deserialize` accept the similarly named `serde` attribute + | +LL | #[serde(untagged)] + | ~~~~~ + +error: cannot find attribute `serde` in this scope + --> $DIR/missing-derive-2.rs:16:7 + | +LL | #[serde(untagged)] + | ^^^^^ + | +note: `serde` is imported here, but it is a crate, not an attribute + --> $DIR/missing-derive-2.rs:5:1 + | +LL | extern crate serde; + | ^^^^^^^^^^^^^^^^^^^ +help: `serde` is an attribute that can be used by the derive macros `Serialize` and `Deserialize`, you might be missing a `derive` attribute + | +LL | #[derive(Serialize, Deserialize)] + | + +error: cannot find attribute `serde` in this scope + --> $DIR/missing-derive-2.rs:8:3 + | +LL | #[serde(untagged)] + | ^^^^^ + | +note: `serde` is imported here, but it is a crate, not an attribute + --> $DIR/missing-derive-2.rs:5:1 + | +LL | extern crate serde; + | ^^^^^^^^^^^^^^^^^^^ +help: `serde` is an attribute that can be used by the derive macros `Serialize` and `Deserialize`, you might be missing a `derive` attribute + | +LL | #[derive(Serialize, Deserialize)] + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui/macros/missing-derive-3.rs b/tests/ui/macros/missing-derive-3.rs new file mode 100644 index 0000000000000..974619e49842f --- /dev/null +++ b/tests/ui/macros/missing-derive-3.rs @@ -0,0 +1,24 @@ +// aux-build:serde.rs + +// derive macros not imported, but namespace imported. Not yet handled. +extern crate serde; + +#[serde(untagged)] //~ ERROR cannot find attribute `serde` +enum A { + A, + B, +} + +enum B { + A, + #[serde(untagged)] //~ ERROR cannot find attribute `serde` + B, +} + +enum C { + A, + #[sede(untagged)] //~ ERROR cannot find attribute `sede` + B, +} + +fn main() {} diff --git a/tests/ui/macros/missing-derive-3.stderr b/tests/ui/macros/missing-derive-3.stderr new file mode 100644 index 0000000000000..0a7ed8d08763c --- /dev/null +++ b/tests/ui/macros/missing-derive-3.stderr @@ -0,0 +1,32 @@ +error: cannot find attribute `sede` in this scope + --> $DIR/missing-derive-3.rs:20:7 + | +LL | #[sede(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; + | ^^^^^^^^^^^^^^^^^^^ + +error: cannot find attribute `serde` in this scope + --> $DIR/missing-derive-3.rs:6:3 + | +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; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/proc-macro/derive-helper-legacy-limits.stderr b/tests/ui/proc-macro/derive-helper-legacy-limits.stderr index 186f38a00f917..7e857a99c4248 100644 --- a/tests/ui/proc-macro/derive-helper-legacy-limits.stderr +++ b/tests/ui/proc-macro/derive-helper-legacy-limits.stderr @@ -3,6 +3,11 @@ 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)] + | error: aborting due to previous error diff --git a/tests/ui/proc-macro/derive-helper-shadowing.stderr b/tests/ui/proc-macro/derive-helper-shadowing.stderr index de2c27a878c67..297854a3cc309 100644 --- a/tests/ui/proc-macro/derive-helper-shadowing.stderr +++ b/tests/ui/proc-macro/derive-helper-shadowing.stderr @@ -16,6 +16,7 @@ error: cannot find attribute `empty_helper` in this scope LL | #[derive(GenHelperUse)] | ^^^^^^^^^^^^ | + = note: `empty_helper` is an attribute that can be used by the derive macro `Empty`, you might be missing a `derive` attribute = help: consider importing this attribute macro: empty_helper = note: this error originates in the derive macro `GenHelperUse` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -29,6 +30,7 @@ LL | #[empty_helper] LL | gen_helper_use!(); | ----------------- in this macro invocation | + = note: `empty_helper` is an attribute that can be used by the derive macro `Empty`, you might be missing a `derive` attribute = help: consider importing this attribute macro: crate::empty_helper = note: this error originates in the macro `gen_helper_use` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/proc-macro/disappearing-resolution.stderr b/tests/ui/proc-macro/disappearing-resolution.stderr index 5b969549a117c..72b620b34429e 100644 --- a/tests/ui/proc-macro/disappearing-resolution.stderr +++ b/tests/ui/proc-macro/disappearing-resolution.stderr @@ -3,6 +3,11 @@ 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)] + | error[E0603]: derive macro import `Empty` is private --> $DIR/disappearing-resolution.rs:11:8