Skip to content

Commit 0aec217

Browse files
authored
Rollup merge of rust-lang#96150 - est31:unused_macro_rules, r=fee1-dead
Implement a lint to warn about unused macro rules This implements a new lint to warn about unused macro rules (arms/matchers), similar to the `unused_macros` lint added by rust-lang#41907 that warns about entire macros. ```rust macro_rules! unused_empty { (hello) => { println!("Hello, world!") }; () => { println!("empty") }; //~ ERROR: 1st rule of macro `unused_empty` is never used } fn main() { unused_empty!(hello); } ``` Builds upon ~~(and currently integrates)~~ rust-lang#96149 and rust-lang#96156. Fixes rust-lang#73576
2 parents 9a92b50 + c347636 commit 0aec217

33 files changed

+382
-70
lines changed

compiler/rustc_codegen_llvm/src/intrinsic.rs

+2
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
816816
span: Span,
817817
) -> Result<&'ll Value, ()> {
818818
// macros for error handling:
819+
#[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
819820
macro_rules! emit_error {
820821
($msg: tt) => {
821822
emit_error!($msg, )
@@ -1144,6 +1145,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
11441145
span: Span,
11451146
args: &[OperandRef<'tcx, &'ll Value>],
11461147
) -> Result<&'ll Value, ()> {
1148+
#[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
11471149
macro_rules! emit_error {
11481150
($msg: tt) => {
11491151
emit_error!($msg, )

compiler/rustc_expand/src/base.rs

+8
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,9 @@ pub struct SyntaxExtension {
711711
/// Built-in macros have a couple of special properties like availability
712712
/// in `#[no_implicit_prelude]` modules, so we have to keep this flag.
713713
pub builtin_name: Option<Symbol>,
714+
/// Rule spans. Used for linting. If the macro is not made up of rules,
715+
/// it's empty.
716+
pub rule_spans: Vec<Span>,
714717
}
715718

716719
impl SyntaxExtension {
@@ -740,6 +743,7 @@ impl SyntaxExtension {
740743
edition,
741744
builtin_name: None,
742745
kind,
746+
rule_spans: Vec::new(),
743747
}
744748
}
745749

@@ -753,6 +757,7 @@ impl SyntaxExtension {
753757
edition: Edition,
754758
name: Symbol,
755759
attrs: &[ast::Attribute],
760+
rule_spans: Vec<Span>,
756761
) -> SyntaxExtension {
757762
let allow_internal_unstable =
758763
attr::allow_internal_unstable(sess, &attrs).collect::<Vec<Symbol>>();
@@ -800,6 +805,7 @@ impl SyntaxExtension {
800805
helper_attrs,
801806
edition,
802807
builtin_name,
808+
rule_spans,
803809
}
804810
}
805811

@@ -887,6 +893,8 @@ pub trait ResolverExpand {
887893
force: bool,
888894
) -> Result<Lrc<SyntaxExtension>, Indeterminate>;
889895

896+
fn record_macro_rule_usage(&mut self, mac_id: NodeId, rule_index: usize);
897+
890898
fn check_unused_macros(&mut self);
891899

892900
// Resolver interfaces for specific built-in macros.

compiler/rustc_expand/src/mbe/macro_rules.rs

+31-15
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ impl<'a> ParserAnyMacro<'a> {
156156
}
157157

158158
struct MacroRulesMacroExpander {
159+
id: NodeId,
159160
name: Ident,
160161
span: Span,
161162
transparency: Transparency,
@@ -179,6 +180,7 @@ impl TTMacroExpander for MacroRulesMacroExpander {
179180
cx,
180181
sp,
181182
self.span,
183+
self.id,
182184
self.name,
183185
self.transparency,
184186
input,
@@ -207,6 +209,7 @@ fn generic_extension<'cx, 'tt>(
207209
cx: &'cx mut ExtCtxt<'_>,
208210
sp: Span,
209211
def_span: Span,
212+
id: NodeId,
210213
name: Ident,
211214
transparency: Transparency,
212215
arg: TokenStream,
@@ -297,6 +300,8 @@ fn generic_extension<'cx, 'tt>(
297300
let mut p = Parser::new(sess, tts, false, None);
298301
p.last_type_ascription = cx.current_expansion.prior_type_ascription;
299302

303+
cx.resolver.record_macro_rule_usage(id, i);
304+
300305
// Let the context choose how to interpret the result.
301306
// Weird, but useful for X-macros.
302307
return Box::new(ParserAnyMacro {
@@ -375,7 +380,7 @@ pub fn compile_declarative_macro(
375380
edition: Edition,
376381
) -> SyntaxExtension {
377382
debug!("compile_declarative_macro: {:?}", def);
378-
let mk_syn_ext = |expander| {
383+
let mk_syn_ext = |expander, rule_spans| {
379384
SyntaxExtension::new(
380385
sess,
381386
SyntaxExtensionKind::LegacyBang(expander),
@@ -384,8 +389,10 @@ pub fn compile_declarative_macro(
384389
edition,
385390
def.ident.name,
386391
&def.attrs,
392+
rule_spans,
387393
)
388394
};
395+
let dummy_syn_ext = || mk_syn_ext(Box::new(macro_rules_dummy_expander), Vec::new());
389396

390397
let diag = &sess.parse_sess.span_diagnostic;
391398
let lhs_nm = Ident::new(sym::lhs, def.span);
@@ -446,17 +453,17 @@ pub fn compile_declarative_macro(
446453
let s = parse_failure_msg(&token);
447454
let sp = token.span.substitute_dummy(def.span);
448455
sess.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit();
449-
return mk_syn_ext(Box::new(macro_rules_dummy_expander));
456+
return dummy_syn_ext();
450457
}
451458
Error(sp, msg) => {
452459
sess.parse_sess
453460
.span_diagnostic
454461
.struct_span_err(sp.substitute_dummy(def.span), &msg)
455462
.emit();
456-
return mk_syn_ext(Box::new(macro_rules_dummy_expander));
463+
return dummy_syn_ext();
457464
}
458465
ErrorReported => {
459-
return mk_syn_ext(Box::new(macro_rules_dummy_expander));
466+
return dummy_syn_ext();
460467
}
461468
};
462469

@@ -531,6 +538,11 @@ pub fn compile_declarative_macro(
531538
None => {}
532539
}
533540

541+
// Compute the spans of the macro rules
542+
// We only take the span of the lhs here,
543+
// so that the spans of created warnings are smaller.
544+
let rule_spans = lhses.iter().map(|lhs| lhs.span()).collect::<Vec<_>>();
545+
534546
// Convert the lhses into `MatcherLoc` form, which is better for doing the
535547
// actual matching. Unless the matcher is invalid.
536548
let lhses = if valid {
@@ -550,17 +562,21 @@ pub fn compile_declarative_macro(
550562
vec![]
551563
};
552564

553-
mk_syn_ext(Box::new(MacroRulesMacroExpander {
554-
name: def.ident,
555-
span: def.span,
556-
transparency,
557-
lhses,
558-
rhses,
559-
valid,
560-
// Macros defined in the current crate have a real node id,
561-
// whereas macros from an external crate have a dummy id.
562-
is_local: def.id != DUMMY_NODE_ID,
563-
}))
565+
mk_syn_ext(
566+
Box::new(MacroRulesMacroExpander {
567+
name: def.ident,
568+
span: def.span,
569+
id: def.id,
570+
transparency,
571+
lhses,
572+
rhses,
573+
valid,
574+
// Macros defined in the current crate have a real node id,
575+
// whereas macros from an external crate have a dummy id.
576+
is_local: def.id != DUMMY_NODE_ID,
577+
}),
578+
rule_spans,
579+
)
564580
}
565581

566582
fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) -> bool {

compiler/rustc_lint/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
303303
PATH_STATEMENTS,
304304
UNUSED_ATTRIBUTES,
305305
UNUSED_MACROS,
306+
UNUSED_MACRO_RULES,
306307
UNUSED_ALLOCATION,
307308
UNUSED_DOC_COMMENTS,
308309
UNUSED_EXTERN_CRATES,

compiler/rustc_lint_defs/src/builtin.rs

+30
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,35 @@ declare_lint! {
775775
"detects macros that were not used"
776776
}
777777

778+
declare_lint! {
779+
/// The `unused_macro_rules` lint detects macro rules that were not used.
780+
///
781+
/// ### Example
782+
///
783+
/// ```rust
784+
/// macro_rules! unused_empty {
785+
/// (hello) => { println!("Hello, world!") };
786+
/// () => { println!("empty") };
787+
/// }
788+
///
789+
/// fn main() {
790+
/// unused_empty!(hello);
791+
/// }
792+
/// ```
793+
///
794+
/// {{produces}}
795+
///
796+
/// ### Explanation
797+
///
798+
/// Unused macro rules may signal a mistake or unfinished code. Furthermore,
799+
/// they slow down compilation. Right now, silencing the warning is not
800+
/// supported on a single rule level, so you have to add an allow to the
801+
/// entire macro definition.
802+
pub UNUSED_MACRO_RULES,
803+
Warn,
804+
"detects macro rules that were not used"
805+
}
806+
778807
declare_lint! {
779808
/// The `warnings` lint allows you to change the level of other
780809
/// lints which produce warnings.
@@ -3138,6 +3167,7 @@ declare_lint_pass! {
31383167
OVERLAPPING_RANGE_ENDPOINTS,
31393168
BINDINGS_WITH_VARIANT_NAME,
31403169
UNUSED_MACROS,
3170+
UNUSED_MACRO_RULES,
31413171
WARNINGS,
31423172
UNUSED_FEATURES,
31433173
STABLE_FEATURES,

compiler/rustc_metadata/src/rmeta/decoder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
932932
self.root.edition,
933933
Symbol::intern(name),
934934
&attrs,
935+
Vec::new(),
935936
)
936937
}
937938

compiler/rustc_middle/src/mir/visit.rs

+3
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ macro_rules! make_mir_visitor {
258258
// for best performance, we want to use an iterator rather
259259
// than a for-loop, to avoid calling `body::Body::invalidate` for
260260
// each basic block.
261+
#[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
261262
macro_rules! basic_blocks {
262263
(mut) => (body.basic_blocks_mut().iter_enumerated_mut());
263264
() => (body.basic_blocks().iter_enumerated());
@@ -279,6 +280,7 @@ macro_rules! make_mir_visitor {
279280
self.visit_local_decl(local, & $($mutability)? body.local_decls[local]);
280281
}
281282

283+
#[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
282284
macro_rules! type_annotations {
283285
(mut) => (body.user_type_annotations.iter_enumerated_mut());
284286
() => (body.user_type_annotations.iter_enumerated());
@@ -932,6 +934,7 @@ macro_rules! make_mir_visitor {
932934
body: &$($mutability)? Body<'tcx>,
933935
location: Location
934936
) {
937+
#[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
935938
macro_rules! basic_blocks {
936939
(mut) => (body.basic_blocks_mut());
937940
() => (body.basic_blocks());

compiler/rustc_resolve/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ version = "0.0.0"
44
edition = "2021"
55

66
[lib]
7-
test = false
87
doctest = false
98

109
[dependencies]

compiler/rustc_resolve/src/build_reduced_graph.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -1218,9 +1218,18 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
12181218
// Mark the given macro as unused unless its name starts with `_`.
12191219
// Macro uses will remove items from this set, and the remaining
12201220
// items will be reported as `unused_macros`.
1221-
fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: NodeId) {
1221+
fn insert_unused_macro(
1222+
&mut self,
1223+
ident: Ident,
1224+
def_id: LocalDefId,
1225+
node_id: NodeId,
1226+
rule_count: usize,
1227+
) {
12221228
if !ident.as_str().starts_with('_') {
12231229
self.r.unused_macros.insert(def_id, (node_id, ident));
1230+
for rule_i in 0..rule_count {
1231+
self.r.unused_macro_rules.insert((def_id, rule_i), (node_id, ident));
1232+
}
12241233
}
12251234
}
12261235

@@ -1244,6 +1253,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
12441253
};
12451254

12461255
let res = Res::Def(DefKind::Macro(ext.macro_kind()), def_id.to_def_id());
1256+
let rule_count = ext.rule_spans.len();
12471257
self.r.macro_map.insert(def_id.to_def_id(), ext);
12481258
self.r.local_macro_def_scopes.insert(def_id, parent_scope.module);
12491259

@@ -1264,7 +1274,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
12641274
self.r.define(module, ident, MacroNS, (res, vis, span, expansion, IsMacroExport));
12651275
} else {
12661276
self.r.check_reserved_macro_name(ident, res);
1267-
self.insert_unused_macro(ident, def_id, item.id);
1277+
self.insert_unused_macro(ident, def_id, item.id, rule_count);
12681278
}
12691279
self.r.visibilities.insert(def_id, vis);
12701280
self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
@@ -1285,7 +1295,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
12851295
_ => self.resolve_visibility(&item.vis),
12861296
};
12871297
if vis != ty::Visibility::Public {
1288-
self.insert_unused_macro(ident, def_id, item.id);
1298+
self.insert_unused_macro(ident, def_id, item.id, rule_count);
12891299
}
12901300
self.r.define(module, ident, MacroNS, (res, vis, span, expansion));
12911301
self.r.visibilities.insert(def_id, vis);

compiler/rustc_resolve/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,7 @@ pub struct Resolver<'a> {
969969
local_macro_def_scopes: FxHashMap<LocalDefId, Module<'a>>,
970970
ast_transform_scopes: FxHashMap<LocalExpnId, Module<'a>>,
971971
unused_macros: FxHashMap<LocalDefId, (NodeId, Ident)>,
972+
unused_macro_rules: FxHashMap<(LocalDefId, usize), (NodeId, Ident)>,
972973
proc_macro_stubs: FxHashSet<LocalDefId>,
973974
/// Traces collected during macro resolution and validated when it's complete.
974975
single_segment_macro_resolutions:
@@ -1354,6 +1355,7 @@ impl<'a> Resolver<'a> {
13541355
potentially_unused_imports: Vec::new(),
13551356
struct_constructors: Default::default(),
13561357
unused_macros: Default::default(),
1358+
unused_macro_rules: Default::default(),
13571359
proc_macro_stubs: Default::default(),
13581360
single_segment_macro_resolutions: Default::default(),
13591361
multi_segment_macro_resolutions: Default::default(),

0 commit comments

Comments
 (0)