diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index cf9cf1b70aaa7..4407297c943a8 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -816,6 +816,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span: Span, ) -> Result<&'ll Value, ()> { // macros for error handling: + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! emit_error { ($msg: tt) => { emit_error!($msg, ) @@ -1144,6 +1145,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span: Span, args: &[OperandRef<'tcx, &'ll Value>], ) -> Result<&'ll Value, ()> { + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! emit_error { ($msg: tt) => { emit_error!($msg, ) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 3799623563f83..e4f9228e1a3ea 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -711,6 +711,9 @@ pub struct SyntaxExtension { /// Built-in macros have a couple of special properties like availability /// in `#[no_implicit_prelude]` modules, so we have to keep this flag. pub builtin_name: Option, + /// Rule spans. Used for linting. If the macro is not made up of rules, + /// it's empty. + pub rule_spans: Vec, } impl SyntaxExtension { @@ -740,6 +743,7 @@ impl SyntaxExtension { edition, builtin_name: None, kind, + rule_spans: Vec::new(), } } @@ -753,6 +757,7 @@ impl SyntaxExtension { edition: Edition, name: Symbol, attrs: &[ast::Attribute], + rule_spans: Vec, ) -> SyntaxExtension { let allow_internal_unstable = attr::allow_internal_unstable(sess, &attrs).collect::>(); @@ -800,6 +805,7 @@ impl SyntaxExtension { helper_attrs, edition, builtin_name, + rule_spans, } } @@ -887,6 +893,8 @@ pub trait ResolverExpand { force: bool, ) -> Result, Indeterminate>; + fn record_macro_rule_usage(&mut self, mac_id: NodeId, rule_index: usize); + fn check_unused_macros(&mut self); // Resolver interfaces for specific built-in macros. diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 2cfd6968accf7..35b5f2dec6f35 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -156,6 +156,7 @@ impl<'a> ParserAnyMacro<'a> { } struct MacroRulesMacroExpander { + id: NodeId, name: Ident, span: Span, transparency: Transparency, @@ -179,6 +180,7 @@ impl TTMacroExpander for MacroRulesMacroExpander { cx, sp, self.span, + self.id, self.name, self.transparency, input, @@ -207,6 +209,7 @@ fn generic_extension<'cx, 'tt>( cx: &'cx mut ExtCtxt<'_>, sp: Span, def_span: Span, + id: NodeId, name: Ident, transparency: Transparency, arg: TokenStream, @@ -297,6 +300,8 @@ fn generic_extension<'cx, 'tt>( let mut p = Parser::new(sess, tts, false, None); p.last_type_ascription = cx.current_expansion.prior_type_ascription; + cx.resolver.record_macro_rule_usage(id, i); + // Let the context choose how to interpret the result. // Weird, but useful for X-macros. return Box::new(ParserAnyMacro { @@ -375,7 +380,7 @@ pub fn compile_declarative_macro( edition: Edition, ) -> SyntaxExtension { debug!("compile_declarative_macro: {:?}", def); - let mk_syn_ext = |expander| { + let mk_syn_ext = |expander, rule_spans| { SyntaxExtension::new( sess, SyntaxExtensionKind::LegacyBang(expander), @@ -384,8 +389,10 @@ pub fn compile_declarative_macro( edition, def.ident.name, &def.attrs, + rule_spans, ) }; + let dummy_syn_ext = || mk_syn_ext(Box::new(macro_rules_dummy_expander), Vec::new()); let diag = &sess.parse_sess.span_diagnostic; let lhs_nm = Ident::new(sym::lhs, def.span); @@ -446,17 +453,17 @@ pub fn compile_declarative_macro( let s = parse_failure_msg(&token); let sp = token.span.substitute_dummy(def.span); sess.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit(); - return mk_syn_ext(Box::new(macro_rules_dummy_expander)); + return dummy_syn_ext(); } Error(sp, msg) => { sess.parse_sess .span_diagnostic .struct_span_err(sp.substitute_dummy(def.span), &msg) .emit(); - return mk_syn_ext(Box::new(macro_rules_dummy_expander)); + return dummy_syn_ext(); } ErrorReported => { - return mk_syn_ext(Box::new(macro_rules_dummy_expander)); + return dummy_syn_ext(); } }; @@ -531,6 +538,11 @@ pub fn compile_declarative_macro( None => {} } + // Compute the spans of the macro rules + // We only take the span of the lhs here, + // so that the spans of created warnings are smaller. + let rule_spans = lhses.iter().map(|lhs| lhs.span()).collect::>(); + // Convert the lhses into `MatcherLoc` form, which is better for doing the // actual matching. Unless the matcher is invalid. let lhses = if valid { @@ -550,17 +562,21 @@ pub fn compile_declarative_macro( vec![] }; - mk_syn_ext(Box::new(MacroRulesMacroExpander { - name: def.ident, - span: def.span, - transparency, - lhses, - rhses, - valid, - // Macros defined in the current crate have a real node id, - // whereas macros from an external crate have a dummy id. - is_local: def.id != DUMMY_NODE_ID, - })) + mk_syn_ext( + Box::new(MacroRulesMacroExpander { + name: def.ident, + span: def.span, + id: def.id, + transparency, + lhses, + rhses, + valid, + // Macros defined in the current crate have a real node id, + // whereas macros from an external crate have a dummy id. + is_local: def.id != DUMMY_NODE_ID, + }), + rule_spans, + ) } fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) -> bool { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 18f229564c2a3..1886debde9b4d 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -303,6 +303,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { PATH_STATEMENTS, UNUSED_ATTRIBUTES, UNUSED_MACROS, + UNUSED_MACRO_RULES, UNUSED_ALLOCATION, UNUSED_DOC_COMMENTS, UNUSED_EXTERN_CRATES, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a42e3d5d95785..a1f8a710f8896 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -775,6 +775,35 @@ declare_lint! { "detects macros that were not used" } +declare_lint! { + /// The `unused_macro_rules` lint detects macro rules that were not used. + /// + /// ### Example + /// + /// ```rust + /// macro_rules! unused_empty { + /// (hello) => { println!("Hello, world!") }; + /// () => { println!("empty") }; + /// } + /// + /// fn main() { + /// unused_empty!(hello); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused macro rules may signal a mistake or unfinished code. Furthermore, + /// they slow down compilation. Right now, silencing the warning is not + /// supported on a single rule level, so you have to add an allow to the + /// entire macro definition. + pub UNUSED_MACRO_RULES, + Warn, + "detects macro rules that were not used" +} + declare_lint! { /// The `warnings` lint allows you to change the level of other /// lints which produce warnings. @@ -3138,6 +3167,7 @@ declare_lint_pass! { OVERLAPPING_RANGE_ENDPOINTS, BINDINGS_WITH_VARIANT_NAME, UNUSED_MACROS, + UNUSED_MACRO_RULES, WARNINGS, UNUSED_FEATURES, STABLE_FEATURES, diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 1edb62e189f97..8765880e2a27c 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -932,6 +932,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.root.edition, Symbol::intern(name), &attrs, + Vec::new(), ) } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 45b1ad6df8226..e2d70dd0b9c41 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -258,6 +258,7 @@ macro_rules! make_mir_visitor { // for best performance, we want to use an iterator rather // than a for-loop, to avoid calling `body::Body::invalidate` for // each basic block. + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! basic_blocks { (mut) => (body.basic_blocks_mut().iter_enumerated_mut()); () => (body.basic_blocks().iter_enumerated()); @@ -279,6 +280,7 @@ macro_rules! make_mir_visitor { self.visit_local_decl(local, & $($mutability)? body.local_decls[local]); } + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! type_annotations { (mut) => (body.user_type_annotations.iter_enumerated_mut()); () => (body.user_type_annotations.iter_enumerated()); @@ -932,6 +934,7 @@ macro_rules! make_mir_visitor { body: &$($mutability)? Body<'tcx>, location: Location ) { + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! basic_blocks { (mut) => (body.basic_blocks_mut()); () => (body.basic_blocks()); diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index bd27c16c732a9..b2178ff59954b 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [lib] -test = false doctest = false [dependencies] diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 291b6645d9aa6..8c242524ba773 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1218,9 +1218,18 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // Mark the given macro as unused unless its name starts with `_`. // Macro uses will remove items from this set, and the remaining // items will be reported as `unused_macros`. - fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: NodeId) { + fn insert_unused_macro( + &mut self, + ident: Ident, + def_id: LocalDefId, + node_id: NodeId, + rule_count: usize, + ) { if !ident.as_str().starts_with('_') { self.r.unused_macros.insert(def_id, (node_id, ident)); + for rule_i in 0..rule_count { + self.r.unused_macro_rules.insert((def_id, rule_i), (node_id, ident)); + } } } @@ -1244,6 +1253,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { }; let res = Res::Def(DefKind::Macro(ext.macro_kind()), def_id.to_def_id()); + let rule_count = ext.rule_spans.len(); self.r.macro_map.insert(def_id.to_def_id(), ext); self.r.local_macro_def_scopes.insert(def_id, parent_scope.module); @@ -1264,7 +1274,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { self.r.define(module, ident, MacroNS, (res, vis, span, expansion, IsMacroExport)); } else { self.r.check_reserved_macro_name(ident, res); - self.insert_unused_macro(ident, def_id, item.id); + self.insert_unused_macro(ident, def_id, item.id, rule_count); } self.r.visibilities.insert(def_id, vis); self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding( @@ -1285,7 +1295,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { _ => self.resolve_visibility(&item.vis), }; if vis != ty::Visibility::Public { - self.insert_unused_macro(ident, def_id, item.id); + self.insert_unused_macro(ident, def_id, item.id, rule_count); } self.r.define(module, ident, MacroNS, (res, vis, span, expansion)); self.r.visibilities.insert(def_id, vis); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index cca1f10258668..54db3c52be883 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -969,6 +969,7 @@ pub struct Resolver<'a> { local_macro_def_scopes: FxHashMap>, ast_transform_scopes: FxHashMap>, unused_macros: FxHashMap, + unused_macro_rules: FxHashMap<(LocalDefId, usize), (NodeId, Ident)>, proc_macro_stubs: FxHashSet, /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: @@ -1354,6 +1355,7 @@ impl<'a> Resolver<'a> { potentially_unused_imports: Vec::new(), 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(), diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 01f0b11f1ac3b..3457a263e216c 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -22,7 +22,9 @@ use rustc_hir::def::{self, DefKind, NonMacroAttrKind}; use rustc_hir::def_id::{CrateNum, LocalDefId}; use rustc_middle::middle::stability; use rustc_middle::ty::RegisteredTools; -use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNUSED_MACROS}; +use rustc_session::lint::builtin::{ + LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNUSED_MACROS, UNUSED_MACRO_RULES, +}; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::feature_err; use rustc_session::Session; @@ -36,6 +38,9 @@ use std::mem; type Res = def::Res; +#[cfg(test)] +mod tests; + /// Binding produced by a `macro_rules` item. /// Not modularized, can shadow previous `macro_rules` bindings, etc. #[derive(Debug)] @@ -311,6 +316,12 @@ impl<'a> ResolverExpand for Resolver<'a> { Ok(ext) } + fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) { + if let Some(did) = self.opt_local_def_id(id) { + self.unused_macro_rules.remove(&(did, rule_i)); + } + } + fn check_unused_macros(&mut self) { for (_, &(node_id, ident)) in self.unused_macros.iter() { self.lint_buffer.buffer_lint( @@ -320,6 +331,24 @@ impl<'a> ResolverExpand for Resolver<'a> { &format!("unused macro definition: `{}`", ident.as_str()), ); } + for (&(def_id, arm_i), &(node_id, ident)) in self.unused_macro_rules.iter() { + if self.unused_macros.contains_key(&def_id) { + // We already lint the entire macro as unused + continue; + } + let ext = &self.macro_map[&def_id.to_def_id()]; + let rule_span = ext.rule_spans[arm_i]; + self.lint_buffer.buffer_lint( + UNUSED_MACRO_RULES, + node_id, + rule_span, + &format!( + "{} rule of macro `{}` is never used", + ordinalize(arm_i + 1), + ident.as_str() + ), + ); + } } fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool { @@ -877,3 +906,14 @@ impl<'a> Resolver<'a> { result } } + +/// Convert the given number into the corresponding ordinal +fn ordinalize(v: usize) -> String { + let suffix = match ((11..=13).contains(&(v % 100)), v % 10) { + (false, 1) => "st", + (false, 2) => "nd", + (false, 3) => "rd", + _ => "th", + }; + format!("{v}{suffix}") +} diff --git a/compiler/rustc_resolve/src/macros/tests.rs b/compiler/rustc_resolve/src/macros/tests.rs new file mode 100644 index 0000000000000..2aa6cc61e460a --- /dev/null +++ b/compiler/rustc_resolve/src/macros/tests.rs @@ -0,0 +1,40 @@ +use super::ordinalize; + +#[test] +fn test_ordinalize() { + assert_eq!(ordinalize(1), "1st"); + assert_eq!(ordinalize(2), "2nd"); + assert_eq!(ordinalize(3), "3rd"); + assert_eq!(ordinalize(4), "4th"); + assert_eq!(ordinalize(5), "5th"); + // ... + assert_eq!(ordinalize(10), "10th"); + assert_eq!(ordinalize(11), "11th"); + assert_eq!(ordinalize(12), "12th"); + assert_eq!(ordinalize(13), "13th"); + assert_eq!(ordinalize(14), "14th"); + // ... + assert_eq!(ordinalize(20), "20th"); + assert_eq!(ordinalize(21), "21st"); + assert_eq!(ordinalize(22), "22nd"); + assert_eq!(ordinalize(23), "23rd"); + assert_eq!(ordinalize(24), "24th"); + // ... + assert_eq!(ordinalize(30), "30th"); + assert_eq!(ordinalize(31), "31st"); + assert_eq!(ordinalize(32), "32nd"); + assert_eq!(ordinalize(33), "33rd"); + assert_eq!(ordinalize(34), "34th"); + // ... + assert_eq!(ordinalize(7010), "7010th"); + assert_eq!(ordinalize(7011), "7011th"); + assert_eq!(ordinalize(7012), "7012th"); + assert_eq!(ordinalize(7013), "7013th"); + assert_eq!(ordinalize(7014), "7014th"); + // ... + assert_eq!(ordinalize(7020), "7020th"); + assert_eq!(ordinalize(7021), "7021st"); + assert_eq!(ordinalize(7022), "7022nd"); + assert_eq!(ordinalize(7023), "7023rd"); + assert_eq!(ordinalize(7024), "7024th"); +} diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 22c19243e7f53..093b02113c3af 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -56,6 +56,7 @@ macro_rules! vec { // `slice::into_vec` function which is only available with cfg(test) // NB see the slice::hack module in slice.rs for more information #[cfg(all(not(no_global_oom_handling), test))] +#[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! vec { () => ( $crate::vec::Vec::new() diff --git a/src/test/ui/consts/const-float-bits-conv.rs b/src/test/ui/consts/const-float-bits-conv.rs index 310db2174aafa..fd5e42ef17077 100644 --- a/src/test/ui/consts/const-float-bits-conv.rs +++ b/src/test/ui/consts/const-float-bits-conv.rs @@ -3,6 +3,7 @@ #![feature(const_float_bits_conv)] #![feature(const_float_classify)] +#![allow(unused_macro_rules)] // Don't promote const fn nop(x: T) -> T { x } diff --git a/src/test/ui/lint/unused/unused-macro-rules-decl.rs b/src/test/ui/lint/unused/unused-macro-rules-decl.rs new file mode 100644 index 0000000000000..537c84940fd01 --- /dev/null +++ b/src/test/ui/lint/unused/unused-macro-rules-decl.rs @@ -0,0 +1,49 @@ +#![feature(decl_macro)] +#![deny(unused_macro_rules)] +// To make sure we are not hitting this +#![deny(unused_macros)] + +// Most simple case +macro num { + (one) => { 1 }, + (two) => { 2 }, //~ ERROR: 2nd rule of macro + (three) => { 3 }, + (four) => { 4 }, //~ ERROR: 4th rule of macro +} +const _NUM: u8 = num!(one) + num!(three); + +// Check that allowing the lint works +#[allow(unused_macro_rules)] +macro num_allowed { + (one) => { 1 }, + (two) => { 2 }, + (three) => { 3 }, + (four) => { 4 }, +} +const _NUM_ALLOWED: u8 = num_allowed!(one) + num_allowed!(three); + +// Check that macro calls inside the macro trigger as usage +macro num_rec { + (one) => { 1 }, + (two) => { + num_rec!(one) + num_rec!(one) + }, + (three) => { //~ ERROR: 3rd rule of macro + num_rec!(one) + num_rec!(two) + }, + (four) => { + num_rec!(two) + num_rec!(two) + }, +} +const _NUM_RECURSIVE: u8 = num_rec!(four); + +// No error if the macro is public +pub macro num_public { + (one) => { 1 }, + (two) => { 2 }, + (three) => { 3 }, + (four) => { 4 }, +} +const _NUM_PUBLIC: u8 = num_public!(one) + num_public!(three); + +fn main() {} diff --git a/src/test/ui/lint/unused/unused-macro-rules-decl.stderr b/src/test/ui/lint/unused/unused-macro-rules-decl.stderr new file mode 100644 index 0000000000000..4d9b22feda2a2 --- /dev/null +++ b/src/test/ui/lint/unused/unused-macro-rules-decl.stderr @@ -0,0 +1,26 @@ +error: 4th rule of macro `num` is never used + --> $DIR/unused-macro-rules-decl.rs:11:5 + | +LL | (four) => { 4 }, + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-macro-rules-decl.rs:2:9 + | +LL | #![deny(unused_macro_rules)] + | ^^^^^^^^^^^^^^^^^^ + +error: 2nd rule of macro `num` is never used + --> $DIR/unused-macro-rules-decl.rs:9:5 + | +LL | (two) => { 2 }, + | ^^^^^ + +error: 3rd rule of macro `num_rec` is never used + --> $DIR/unused-macro-rules-decl.rs:31:5 + | +LL | (three) => { + | ^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/lint/unused/unused-macro-rules.rs b/src/test/ui/lint/unused/unused-macro-rules.rs index 1a714b8f0a0d0..eeaf4d1b0a9ce 100644 --- a/src/test/ui/lint/unused/unused-macro-rules.rs +++ b/src/test/ui/lint/unused/unused-macro-rules.rs @@ -1,29 +1,47 @@ +#![deny(unused_macro_rules)] +// To make sure we are not hitting this #![deny(unused_macros)] // Most simple case -macro_rules! unused { //~ ERROR: unused macro definition - () => {}; +macro_rules! num { + (one) => { 1 }; + (two) => { 2 }; //~ ERROR: 2nd rule of macro + (three) => { 3 }; + (four) => { 4 }; //~ ERROR: 4th rule of macro } +const _NUM: u8 = num!(one) + num!(three); -// Test macros created by macros -macro_rules! create_macro { - () => { - macro_rules! m { //~ ERROR: unused macro definition - () => {}; - } - }; +// Check that allowing the lint works +#[allow(unused_macro_rules)] +macro_rules! num_allowed { + (one) => { 1 }; + (two) => { 2 }; + (three) => { 3 }; + (four) => { 4 }; } -create_macro!(); +const _NUM_ALLOWED: u8 = num_allowed!(one) + num_allowed!(three); -#[allow(unused_macros)] -mod bar { - // Test that putting the #[deny] close to the macro's definition - // works. +// Check that macro calls inside the macro trigger as usage +macro_rules! num_rec { + (one) => { 1 }; + (two) => { + num_rec!(one) + num_rec!(one) + }; + (three) => { //~ ERROR: 3rd rule of macro + num_rec!(one) + num_rec!(two) + }; + (four) => { num_rec!(two) + num_rec!(two) }; +} +const _NUM_RECURSIVE: u8 = num_rec!(four); - #[deny(unused_macros)] - macro_rules! unused { //~ ERROR: unused macro definition - () => {}; - } +// No error if the macro is being exported +#[macro_export] +macro_rules! num_exported { + (one) => { 1 }; + (two) => { 2 }; + (three) => { 3 }; + (four) => { 4 }; } +const _NUM_EXPORTED: u8 = num_exported!(one) + num_exported!(three); fn main() {} diff --git a/src/test/ui/lint/unused/unused-macro-rules.stderr b/src/test/ui/lint/unused/unused-macro-rules.stderr index 59db35b411183..2b3098a5128d4 100644 --- a/src/test/ui/lint/unused/unused-macro-rules.stderr +++ b/src/test/ui/lint/unused/unused-macro-rules.stderr @@ -1,32 +1,26 @@ -error: unused macro definition: `unused` - --> $DIR/unused-macro-rules.rs:4:14 +error: 4th rule of macro `num` is never used + --> $DIR/unused-macro-rules.rs:10:5 | -LL | macro_rules! unused { - | ^^^^^^ +LL | (four) => { 4 }; + | ^^^^^^ | note: the lint level is defined here --> $DIR/unused-macro-rules.rs:1:9 | -LL | #![deny(unused_macros)] - | ^^^^^^^^^^^^^ +LL | #![deny(unused_macro_rules)] + | ^^^^^^^^^^^^^^^^^^ -error: unused macro definition: `m` - --> $DIR/unused-macro-rules.rs:11:22 +error: 2nd rule of macro `num` is never used + --> $DIR/unused-macro-rules.rs:8:5 | -LL | macro_rules! m { - | ^ +LL | (two) => { 2 }; + | ^^^^^ -error: unused macro definition: `unused` - --> $DIR/unused-macro-rules.rs:24:18 +error: 3rd rule of macro `num_rec` is never used + --> $DIR/unused-macro-rules.rs:30:5 | -LL | macro_rules! unused { - | ^^^^^^ - | -note: the lint level is defined here - --> $DIR/unused-macro-rules.rs:23:12 - | -LL | #[deny(unused_macros)] - | ^^^^^^^^^^^^^ +LL | (three) => { + | ^^^^^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/lint/unused/unused-macro.rs b/src/test/ui/lint/unused/unused-macros-decl.rs similarity index 87% rename from src/test/ui/lint/unused/unused-macro.rs rename to src/test/ui/lint/unused/unused-macros-decl.rs index 302b0727d77b0..21f6108b18ad5 100644 --- a/src/test/ui/lint/unused/unused-macro.rs +++ b/src/test/ui/lint/unused/unused-macros-decl.rs @@ -1,5 +1,7 @@ #![feature(decl_macro)] #![deny(unused_macros)] +// To make sure we are not hitting this +#![deny(unused_macro_rules)] // Most simple case macro unused { //~ ERROR: unused macro definition diff --git a/src/test/ui/lint/unused/unused-macro.stderr b/src/test/ui/lint/unused/unused-macros-decl.stderr similarity index 74% rename from src/test/ui/lint/unused/unused-macro.stderr rename to src/test/ui/lint/unused/unused-macros-decl.stderr index 1a73279ed6dbd..1f426b9d91a61 100644 --- a/src/test/ui/lint/unused/unused-macro.stderr +++ b/src/test/ui/lint/unused/unused-macros-decl.stderr @@ -1,29 +1,29 @@ error: unused macro definition: `unused` - --> $DIR/unused-macro.rs:5:7 + --> $DIR/unused-macros-decl.rs:7:7 | LL | macro unused { | ^^^^^^ | note: the lint level is defined here - --> $DIR/unused-macro.rs:2:9 + --> $DIR/unused-macros-decl.rs:2:9 | LL | #![deny(unused_macros)] | ^^^^^^^^^^^^^ error: unused macro definition: `unused` - --> $DIR/unused-macro.rs:15:11 + --> $DIR/unused-macros-decl.rs:17:11 | LL | macro unused { | ^^^^^^ | note: the lint level is defined here - --> $DIR/unused-macro.rs:14:12 + --> $DIR/unused-macros-decl.rs:16:12 | LL | #[deny(unused_macros)] | ^^^^^^^^^^^^^ error: unused macro definition: `unused` - --> $DIR/unused-macro.rs:21:22 + --> $DIR/unused-macros-decl.rs:23:22 | LL | pub(crate) macro unused { | ^^^^^^ diff --git a/src/test/ui/lint/unused/unused-macros.rs b/src/test/ui/lint/unused/unused-macros.rs new file mode 100644 index 0000000000000..70b50b2082b36 --- /dev/null +++ b/src/test/ui/lint/unused/unused-macros.rs @@ -0,0 +1,31 @@ +#![deny(unused_macros)] +// To make sure we are not hitting this +#![deny(unused_macro_rules)] + +// Most simple case +macro_rules! unused { //~ ERROR: unused macro definition + () => {}; +} + +// Test macros created by macros +macro_rules! create_macro { + () => { + macro_rules! m { //~ ERROR: unused macro definition + () => {}; + } + }; +} +create_macro!(); + +#[allow(unused_macros)] +mod bar { + // Test that putting the #[deny] close to the macro's definition + // works. + + #[deny(unused_macros)] + macro_rules! unused { //~ ERROR: unused macro definition + () => {}; + } +} + +fn main() {} diff --git a/src/test/ui/lint/unused/unused-macros.stderr b/src/test/ui/lint/unused/unused-macros.stderr new file mode 100644 index 0000000000000..d0baf5becec4c --- /dev/null +++ b/src/test/ui/lint/unused/unused-macros.stderr @@ -0,0 +1,32 @@ +error: unused macro definition: `unused` + --> $DIR/unused-macros.rs:6:14 + | +LL | macro_rules! unused { + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-macros.rs:1:9 + | +LL | #![deny(unused_macros)] + | ^^^^^^^^^^^^^ + +error: unused macro definition: `m` + --> $DIR/unused-macros.rs:13:22 + | +LL | macro_rules! m { + | ^ + +error: unused macro definition: `unused` + --> $DIR/unused-macros.rs:26:18 + | +LL | macro_rules! unused { + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-macros.rs:25:12 + | +LL | #[deny(unused_macros)] + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/macros/issue-41803.rs b/src/test/ui/macros/issue-41803.rs index 19ab81d04d0eb..bccfdc611460e 100644 --- a/src/test/ui/macros/issue-41803.rs +++ b/src/test/ui/macros/issue-41803.rs @@ -1,4 +1,6 @@ // run-pass +#![allow(unused_macro_rules)] + /// A compile-time map from identifiers to arbitrary (heterogeneous) expressions macro_rules! ident_map { ( $name:ident = { $($key:ident => $e:expr,)* } ) => { diff --git a/src/test/ui/macros/issue-52169.rs b/src/test/ui/macros/issue-52169.rs index 60be97f0aee23..f178cd30cb499 100644 --- a/src/test/ui/macros/issue-52169.rs +++ b/src/test/ui/macros/issue-52169.rs @@ -1,5 +1,6 @@ // run-pass +#[allow(unused_macro_rules)] macro_rules! a { ($i:literal) => { "right" }; ($i:tt) => { "wrong" }; diff --git a/src/test/ui/macros/macro-first-set.rs b/src/test/ui/macros/macro-first-set.rs index f85376dabcb5d..eeb1ddd84ae58 100644 --- a/src/test/ui/macros/macro-first-set.rs +++ b/src/test/ui/macros/macro-first-set.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_macro_rules)] //{{{ issue 40569 ============================================================== diff --git a/src/test/ui/macros/macro-literal.rs b/src/test/ui/macros/macro-literal.rs index e08d0a67b4345..3c2e71f9c43f3 100644 --- a/src/test/ui/macros/macro-literal.rs +++ b/src/test/ui/macros/macro-literal.rs @@ -21,6 +21,7 @@ macro_rules! only_expr { }; } +#[allow(unused_macro_rules)] macro_rules! mtester_dbg { ($l:literal) => { &format!("macro caught literal: {:?}", $l) diff --git a/src/test/ui/macros/macro-pub-matcher.rs b/src/test/ui/macros/macro-pub-matcher.rs index c02e6794edbc0..174056d6cdfaf 100644 --- a/src/test/ui/macros/macro-pub-matcher.rs +++ b/src/test/ui/macros/macro-pub-matcher.rs @@ -1,5 +1,5 @@ // run-pass -#![allow(dead_code, unused_imports)] +#![allow(dead_code, unused_imports, unused_macro_rules)] #![feature(crate_visibility_modifier)] /** diff --git a/src/test/ui/macros/stmt_expr_attr_macro_parse.rs b/src/test/ui/macros/stmt_expr_attr_macro_parse.rs index 3ab50db0ea1be..570191d2c90ae 100644 --- a/src/test/ui/macros/stmt_expr_attr_macro_parse.rs +++ b/src/test/ui/macros/stmt_expr_attr_macro_parse.rs @@ -1,4 +1,6 @@ // run-pass +#![allow(unused_macro_rules)] + macro_rules! m { ($e:expr) => { "expr includes attr" diff --git a/src/test/ui/macros/type-macros-hlist.rs b/src/test/ui/macros/type-macros-hlist.rs index 77d866cea9ceb..946b5bd5d9334 100644 --- a/src/test/ui/macros/type-macros-hlist.rs +++ b/src/test/ui/macros/type-macros-hlist.rs @@ -1,4 +1,6 @@ // run-pass +#![allow(unused_macro_rules)] + use std::ops::*; #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] diff --git a/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs b/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs index 9d68a7bffdeee..76c07bbfd8106 100644 --- a/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs +++ b/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs @@ -3,6 +3,7 @@ // Test that failing macro matchers will not cause pre-expansion errors // even though they use a feature that is pre-expansion gated. +#[allow(unused_macro_rules)] macro_rules! m { ($e:expr) => { 0 }; // This fails on the input below due to `, foo`. ($e:expr,) => { 1 }; // This also fails to match due to `foo`. diff --git a/src/test/ui/rust-2018/async-ident.fixed b/src/test/ui/rust-2018/async-ident.fixed index f4ae518c71d27..e909c79070ca7 100644 --- a/src/test/ui/rust-2018/async-ident.fixed +++ b/src/test/ui/rust-2018/async-ident.fixed @@ -1,4 +1,4 @@ -#![allow(dead_code, unused_variables, non_camel_case_types, non_upper_case_globals)] +#![allow(dead_code, unused_variables, unused_macro_rules, bad_style)] #![deny(keyword_idents)] // edition:2015 diff --git a/src/test/ui/rust-2018/async-ident.rs b/src/test/ui/rust-2018/async-ident.rs index 79c73dafac7a3..2bfbc3871d128 100644 --- a/src/test/ui/rust-2018/async-ident.rs +++ b/src/test/ui/rust-2018/async-ident.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, unused_variables, non_camel_case_types, non_upper_case_globals)] +#![allow(dead_code, unused_variables, unused_macro_rules, bad_style)] #![deny(keyword_idents)] // edition:2015 diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs index 51ed1b5a61cbe..1ce02e48c05b6 100644 --- a/src/tools/error_index_generator/main.rs +++ b/src/tools/error_index_generator/main.rs @@ -280,10 +280,6 @@ fn main() { fn register_all() -> Vec<(&'static str, Option<&'static str>)> { let mut long_codes: Vec<(&'static str, Option<&'static str>)> = Vec::new(); macro_rules! register_diagnostics { - ($($ecode:ident: $message:expr,)*) => ( - register_diagnostics!{$($ecode:$message,)* ;} - ); - ($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => ( $( {long_codes.extend([