diff options
| -rw-r--r-- | compiler/rustc_expand/src/base.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/mbe/macro_rules.rs | 40 | ||||
| -rw-r--r-- | compiler/rustc_resolve/Cargo.toml | 1 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/build_reduced_graph.rs | 26 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/diagnostics.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/diagnostics/tests.rs | 40 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/lib.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/macros.rs | 41 |
8 files changed, 139 insertions, 27 deletions
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index ae1b50a4176..3d28da684d1 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -887,6 +887,8 @@ pub trait ResolverExpand { force: bool, ) -> Result<Lrc<SyntaxExtension>, 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 050710097c3..ba0b35470b6 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -156,13 +156,13 @@ impl<'a> ParserAnyMacro<'a> { } struct MacroRulesMacroExpander { + node_id: NodeId, name: Ident, span: Span, transparency: Transparency, lhses: Vec<Vec<MatcherLoc>>, rhses: Vec<mbe::TokenTree>, valid: bool, - is_local: bool, } impl TTMacroExpander for MacroRulesMacroExpander { @@ -179,12 +179,12 @@ impl TTMacroExpander for MacroRulesMacroExpander { cx, sp, self.span, + self.node_id, self.name, self.transparency, input, &self.lhses, &self.rhses, - self.is_local, ) } } @@ -207,14 +207,17 @@ fn generic_extension<'cx, 'tt>( cx: &'cx mut ExtCtxt<'_>, sp: Span, def_span: Span, + node_id: NodeId, name: Ident, transparency: Transparency, arg: TokenStream, lhses: &'tt [Vec<MatcherLoc>], rhses: &'tt [mbe::TokenTree], - is_local: bool, ) -> Box<dyn MacResult + 'cx> { let sess = &cx.sess.parse_sess; + // Macros defined in the current crate have a real node id, + // whereas macros from an external crate have a dummy id. + let is_local = node_id != DUMMY_NODE_ID; if cx.trace_macros() { let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(&arg)); @@ -296,6 +299,10 @@ fn generic_extension<'cx, 'tt>( let mut p = Parser::new(sess, tts, false, None); p.last_type_ascription = cx.current_expansion.prior_type_ascription; + if is_local { + cx.resolver.record_macro_rule_usage(node_id, i); + } + // Let the context choose how to interpret the result. // Weird, but useful for X-macros. return Box::new(ParserAnyMacro { @@ -372,7 +379,7 @@ pub fn compile_declarative_macro( features: &Features, def: &ast::Item, edition: Edition, -) -> SyntaxExtension { +) -> (SyntaxExtension, Vec<Span>) { debug!("compile_declarative_macro: {:?}", def); let mk_syn_ext = |expander| { SyntaxExtension::new( @@ -385,6 +392,7 @@ pub fn compile_declarative_macro( &def.attrs, ) }; + 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); @@ -445,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(); } }; @@ -530,6 +538,15 @@ 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 = if def.id != DUMMY_NODE_ID { + lhses.iter().map(|lhs| lhs.span()).collect::<Vec<_>>() + } else { + Vec::new() + }; + // Convert the lhses into `MatcherLoc` form, which is better for doing the // actual matching. Unless the matcher is invalid. let lhses = if valid { @@ -549,17 +566,16 @@ pub fn compile_declarative_macro( vec![] }; - mk_syn_ext(Box::new(MacroRulesMacroExpander { + let expander = Box::new(MacroRulesMacroExpander { name: def.ident, span: def.span, + node_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, - })) + }); + (mk_syn_ext(expander), rule_spans) } fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) -> bool { diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index bd27c16c732..b2178ff5995 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 783ff5a3f91..e68d6fdeea5 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -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); diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index aef9fb57a6a..bffff0dfac5 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -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}") +} diff --git a/compiler/rustc_resolve/src/diagnostics/tests.rs b/compiler/rustc_resolve/src/diagnostics/tests.rs new file mode 100644 index 00000000000..2aa6cc61e46 --- /dev/null +++ b/compiler/rustc_resolve/src/diagnostics/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/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index f6109b1dc1a..7aa66efc038 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -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(), diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 19a9c1b99fc..2337f72f1e8 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -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) } } |
