Auto merge of #97903 - est31:unused_macro_rules_compile_error, r=petrochenkov
Never regard macro rules with compile_error! invocations as unused The very point of compile_error! is to never be reached, and one of the use cases of the macro, currently also listed as examples in the documentation of compile_error, is to create nicer errors for wrong macro invocations. Thus, we should never warn about unused macro arms that contain invocations of compile_error. See also https://github.com/rust-lang/rust/pull/96150#issuecomment-1126599107 and the discussion after that. Furthermore, the PR also contains two commits to silence `unused_macro_rules` when a macro has an invalid rule, and to add a test that `unused_macros` does not behave badly in the same situation. r? `@petrochenkov` as I've talked to them about this
This commit is contained in:
commit
fa68e73e99
10 changed files with 158 additions and 10 deletions
|
@ -1,4 +1,5 @@
|
|||
#![allow(rustc::potential_query_instability)]
|
||||
#![feature(array_windows)]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(associated_type_defaults)]
|
||||
#![feature(if_let_guard)]
|
||||
|
|
|
@ -380,7 +380,7 @@ pub fn compile_declarative_macro(
|
|||
features: &Features,
|
||||
def: &ast::Item,
|
||||
edition: Edition,
|
||||
) -> (SyntaxExtension, Vec<Span>) {
|
||||
) -> (SyntaxExtension, Vec<(usize, Span)>) {
|
||||
debug!("compile_declarative_macro: {:?}", def);
|
||||
let mk_syn_ext = |expander| {
|
||||
SyntaxExtension::new(
|
||||
|
@ -539,11 +539,22 @@ 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<_>>()
|
||||
// Compute the spans of the macro rules for unused rule linting.
|
||||
// To avoid warning noise, only consider the rules of this
|
||||
// macro for the lint, if all rules are valid.
|
||||
// Also, we are only interested in non-foreign macros.
|
||||
let rule_spans = if valid && def.id != DUMMY_NODE_ID {
|
||||
lhses
|
||||
.iter()
|
||||
.zip(rhses.iter())
|
||||
.enumerate()
|
||||
// If the rhs contains an invocation like compile_error!,
|
||||
// don't consider the rule for the unused rule lint.
|
||||
.filter(|(_idx, (_lhs, rhs))| !has_compile_error_macro(rhs))
|
||||
// We only take the span of the lhs here,
|
||||
// so that the spans of created warnings are smaller.
|
||||
.map(|(idx, (lhs, _rhs))| (idx, lhs.span()))
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
@ -651,6 +662,29 @@ fn check_matcher(sess: &ParseSess, def: &ast::Item, matcher: &[mbe::TokenTree])
|
|||
err == sess.span_diagnostic.err_count()
|
||||
}
|
||||
|
||||
fn has_compile_error_macro(rhs: &mbe::TokenTree) -> bool {
|
||||
match rhs {
|
||||
mbe::TokenTree::Delimited(_sp, d) => {
|
||||
let has_compile_error = d.tts.array_windows::<3>().any(|[ident, bang, args]| {
|
||||
if let mbe::TokenTree::Token(ident) = ident &&
|
||||
let TokenKind::Ident(ident, _) = ident.kind &&
|
||||
ident == sym::compile_error &&
|
||||
let mbe::TokenTree::Token(bang) = bang &&
|
||||
let TokenKind::Not = bang.kind &&
|
||||
let mbe::TokenTree::Delimited(_, del) = args &&
|
||||
del.delim != Delimiter::Invisible
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
if has_compile_error { true } else { d.tts.iter().any(has_compile_error_macro) }
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// `The FirstSets` for a matcher is a mapping from subsequences in the
|
||||
// matcher to the FIRST set for that subsequence.
|
||||
//
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue