Implement the unused_macro_rules lint

This commit is contained in:
est31 2022-04-17 04:01:17 +02:00
parent 3d43be3ad3
commit 0bd2232fe4
8 changed files with 139 additions and 27 deletions

View file

@ -194,7 +194,7 @@ impl<'a> Resolver<'a> {
}
let ext = Lrc::new(match self.cstore().load_macro_untracked(def_id, &self.session) {
LoadedMacro::MacroDef(item, edition) => self.compile_macro(&item, edition),
LoadedMacro::MacroDef(item, edition) => self.compile_macro(&item, edition).0,
LoadedMacro::ProcMacro(ext) => ext,
});
@ -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_spans: &[Span],
) {
if !ident.as_str().starts_with('_') {
self.r.unused_macros.insert(def_id, (node_id, ident));
for (rule_i, rule_span) in rule_spans.iter().enumerate() {
self.r.unused_macro_rules.insert((def_id, rule_i), (ident, *rule_span));
}
}
}
@ -1228,15 +1237,16 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
let parent_scope = self.parent_scope;
let expansion = parent_scope.expansion;
let def_id = self.r.local_def_id(item.id);
let (ext, ident, span, macro_rules) = match &item.kind {
let (ext, ident, span, macro_rules, rule_spans) = match &item.kind {
ItemKind::MacroDef(def) => {
let ext = Lrc::new(self.r.compile_macro(item, self.r.session.edition()));
(ext, item.ident, item.span, def.macro_rules)
let (ext, rule_spans) = self.r.compile_macro(item, self.r.session.edition());
let ext = Lrc::new(ext);
(ext, item.ident, item.span, def.macro_rules, rule_spans)
}
ItemKind::Fn(..) => match self.proc_macro_stub(item) {
Some((macro_kind, ident, span)) => {
self.r.proc_macro_stubs.insert(def_id);
(self.r.dummy_ext(macro_kind), ident, span, false)
(self.r.dummy_ext(macro_kind), ident, span, false, Vec::new())
}
None => return parent_scope.macro_rules,
},
@ -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_spans);
}
self.r.visibilities.insert(def_id, vis);
let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
@ -1287,7 +1297,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_spans);
}
self.r.define(module, ident, MacroNS, (res, vis, span, expansion));
self.r.visibilities.insert(def_id, vis);

View file

@ -35,6 +35,9 @@ use crate::{LexicalScopeBinding, NameBinding, NameBindingKind, PrivacyError, Vis
use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet};
use crate::{Segment, UseError};
#[cfg(test)]
mod tests;
type Res = def::Res<ast::NodeId>;
/// A vector of spans and replacements, a message and applicability.
@ -2663,3 +2666,14 @@ fn is_span_suitable_for_use_injection(s: Span) -> bool {
// import or other generated ones
!s.from_expansion()
}
/// Convert the given number into the corresponding ordinal
crate 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}")
}

View file

@ -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");
}

View file

@ -973,6 +973,7 @@ pub struct Resolver<'a> {
local_macro_def_scopes: FxHashMap<LocalDefId, Module<'a>>,
ast_transform_scopes: FxHashMap<LocalExpnId, Module<'a>>,
unused_macros: FxHashMap<LocalDefId, (NodeId, Ident)>,
unused_macro_rules: FxHashMap<(LocalDefId, usize), (Ident, Span)>,
proc_macro_stubs: FxHashSet<LocalDefId>,
/// Traces collected during macro resolution and validated when it's complete.
single_segment_macro_resolutions:
@ -1372,6 +1373,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(),

View file

@ -22,7 +22,8 @@ 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};
use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES};
use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_session::parse::feature_err;
use rustc_session::Session;
@ -311,6 +312,11 @@ impl<'a> ResolverExpand for Resolver<'a> {
Ok(ext)
}
fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) {
let did = self.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 +326,23 @@ impl<'a> ResolverExpand for Resolver<'a> {
&format!("unused macro definition: `{}`", ident.as_str()),
);
}
for (&(def_id, arm_i), &(ident, rule_span)) in self.unused_macro_rules.iter() {
if self.unused_macros.contains_key(&def_id) {
// We already lint the entire macro as unused
continue;
}
let node_id = self.def_id_to_node_id[def_id];
self.lint_buffer.buffer_lint(
UNUSED_MACRO_RULES,
node_id,
rule_span,
&format!(
"{} rule of macro `{}` is never used",
crate::diagnostics::ordinalize(arm_i + 1),
ident.as_str()
),
);
}
}
fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool {
@ -830,10 +853,15 @@ impl<'a> Resolver<'a> {
}
}
/// Compile the macro into a `SyntaxExtension` and possibly replace
/// its expander to a pre-defined one for built-in macros.
crate fn compile_macro(&mut self, item: &ast::Item, edition: Edition) -> SyntaxExtension {
let mut result = compile_declarative_macro(
/// Compile the macro into a `SyntaxExtension` and its rule spans.
///
/// Possibly replace its expander to a pre-defined one for built-in macros.
crate fn compile_macro(
&mut self,
item: &ast::Item,
edition: Edition,
) -> (SyntaxExtension, Vec<Span>) {
let (mut result, mut rule_spans) = compile_declarative_macro(
&self.session,
self.session.features_untracked(),
item,
@ -849,6 +877,7 @@ impl<'a> Resolver<'a> {
match mem::replace(builtin_macro, BuiltinMacroState::AlreadySeen(item.span)) {
BuiltinMacroState::NotYetSeen(ext) => {
result.kind = ext;
rule_spans = Vec::new();
if item.id != ast::DUMMY_NODE_ID {
self.builtin_macro_kinds
.insert(self.local_def_id(item.id), result.macro_kind());
@ -871,6 +900,6 @@ impl<'a> Resolver<'a> {
}
}
result
(result, rule_spans)
}
}