Auto merge of #86574 - m-ou-se:or-pattern-lint-fix, r=petrochenkov

Don't lint :pat when re-parsing a macro from another crate.

`compile_macro` is used both when compiling the original definition in the crate that defines it, and to compile the macro when loading it when compiling a crate that uses it. We should only emit lints in the first case.

This adds a `is_definition: bool` to pass this information in, so we don't warn about things that only concern the definition site.

Fixes #86567
This commit is contained in:
bors 2021-06-25 01:23:16 +00:00
commit 079aa837d2
5 changed files with 57 additions and 37 deletions

View File

@ -11,7 +11,7 @@ use crate::mbe::transcribe::transcribe;
use rustc_ast as ast;
use rustc_ast::token::{self, NonterminalKind, NtTT, Token, TokenKind::*};
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::NodeId;
use rustc_ast::{NodeId, DUMMY_NODE_ID};
use rustc_ast_pretty::pprust;
use rustc_attr::{self as attr, TransparencyError};
use rustc_data_structures::fx::FxHashMap;
@ -471,7 +471,7 @@ pub fn compile_declarative_macro(
)
.pop()
.unwrap();
valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def.attrs, &tt);
valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def, &tt);
return tt;
}
}
@ -540,13 +540,13 @@ pub fn compile_declarative_macro(
fn check_lhs_nt_follows(
sess: &ParseSess,
features: &Features,
attrs: &[ast::Attribute],
def: &ast::Item,
lhs: &mbe::TokenTree,
) -> bool {
// lhs is going to be like TokenTree::Delimited(...), where the
// entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
if let mbe::TokenTree::Delimited(_, ref tts) = *lhs {
check_matcher(sess, features, attrs, &tts.tts)
check_matcher(sess, features, def, &tts.tts)
} else {
let msg = "invalid macro matcher; matchers must be contained in balanced delimiters";
sess.span_diagnostic.span_err(lhs.span(), msg);
@ -604,13 +604,13 @@ fn check_rhs(sess: &ParseSess, rhs: &mbe::TokenTree) -> bool {
fn check_matcher(
sess: &ParseSess,
features: &Features,
attrs: &[ast::Attribute],
def: &ast::Item,
matcher: &[mbe::TokenTree],
) -> bool {
let first_sets = FirstSets::new(matcher);
let empty_suffix = TokenSet::empty();
let err = sess.span_diagnostic.err_count();
check_matcher_core(sess, features, attrs, &first_sets, matcher, &empty_suffix);
check_matcher_core(sess, features, def, &first_sets, matcher, &empty_suffix);
err == sess.span_diagnostic.err_count()
}
@ -857,7 +857,7 @@ impl TokenSet {
fn check_matcher_core(
sess: &ParseSess,
features: &Features,
attrs: &[ast::Attribute],
def: &ast::Item,
first_sets: &FirstSets,
matcher: &[mbe::TokenTree],
follow: &TokenSet,
@ -903,7 +903,7 @@ fn check_matcher_core(
}
TokenTree::Delimited(span, ref d) => {
let my_suffix = TokenSet::singleton(d.close_tt(span));
check_matcher_core(sess, features, attrs, first_sets, &d.tts, &my_suffix);
check_matcher_core(sess, features, def, first_sets, &d.tts, &my_suffix);
// don't track non NT tokens
last.replace_with_irrelevant();
@ -936,7 +936,7 @@ fn check_matcher_core(
// `my_suffix` is some TokenSet that we can use
// for checking the interior of `seq_rep`.
let next =
check_matcher_core(sess, features, attrs, first_sets, &seq_rep.tts, my_suffix);
check_matcher_core(sess, features, def, first_sets, &seq_rep.tts, my_suffix);
if next.maybe_empty {
last.add_all(&next);
} else {
@ -956,29 +956,31 @@ fn check_matcher_core(
for token in &last.tokens {
if let TokenTree::MetaVarDecl(span, name, Some(kind)) = *token {
for next_token in &suffix_first.tokens {
// Check if the old pat is used and the next token is `|`.
if let NonterminalKind::PatParam { inferred: true } = kind {
if let TokenTree::Token(token) = next_token {
if let BinOp(token) = token.kind {
if let token::BinOpToken::Or = token {
// It is suggestion to use pat_param, for example: $x:pat -> $x:pat_param.
let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
span,
name,
Some(NonterminalKind::PatParam { inferred: false }),
));
sess.buffer_lint_with_diagnostic(
&OR_PATTERNS_BACK_COMPAT,
span,
ast::CRATE_NODE_ID,
&*format!("the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro",),
BuiltinLintDiagnostics::OrPatternsBackCompat(
span, suggestion,
),
);
}
}
}
// Check if the old pat is used and the next token is `|`
// to warn about incompatibility with Rust 2021.
// We only emit this lint if we're parsing the original
// definition of this macro_rules, not while (re)parsing
// the macro when compiling another crate that is using the
// macro. (See #86567.)
// Macros defined in the current crate have a real node id,
// whereas macros from an external crate have a dummy id.
if def.id != DUMMY_NODE_ID
&& matches!(kind, NonterminalKind::PatParam { inferred: true })
&& matches!(next_token, TokenTree::Token(token) if token.kind == BinOp(token::BinOpToken::Or))
{
// It is suggestion to use pat_param, for example: $x:pat -> $x:pat_param.
let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
span,
name,
Some(NonterminalKind::PatParam { inferred: false }),
));
sess.buffer_lint_with_diagnostic(
&OR_PATTERNS_BACK_COMPAT,
span,
ast::CRATE_NODE_ID,
"the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro",
BuiltinLintDiagnostics::OrPatternsBackCompat(span, suggestion),
);
}
match is_in_follow(next_token, kind) {
IsInFollow::Yes => {}

View File

@ -0,0 +1,6 @@
#![crate_type = "lib"]
#[macro_export]
macro_rules! a {
($x:pat|) => ();
}

View File

@ -1,14 +1,19 @@
// run-rustfix
// aux-build:or-pattern.rs
#![deny(or_patterns_back_compat)]
#![allow(unused_macros)]
#[macro_use]
extern crate or_pattern;
macro_rules! foo { ($x:pat_param | $y:pat) => {} }
//~^ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
//~| WARN this was previously accepted
macro_rules! bar { ($($x:pat_param)+ | $($y:pat)+) => {} }
//~^ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
//~| WARN this was previously accepted
macro_rules! baz { ($x:pat_param | $y:pat_param) => {} } // should be ok
macro_rules! qux { ($x:pat_param | $y:pat) => {} } // should be ok
macro_rules! ogg { ($x:pat_param | $y:pat_param) => {} }
@ -30,4 +35,5 @@ fn main() {
let result: Result<i64, i32> = Err(42);
let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into());
assert_eq!(int, 42);
a!(1|);
}

View File

@ -1,14 +1,19 @@
// run-rustfix
// aux-build:or-pattern.rs
#![deny(or_patterns_back_compat)]
#![allow(unused_macros)]
#[macro_use]
extern crate or_pattern;
macro_rules! foo { ($x:pat | $y:pat) => {} }
//~^ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
//~| WARN this was previously accepted
macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} }
//~^ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
//~| WARN this was previously accepted
macro_rules! baz { ($x:pat_param | $y:pat_param) => {} } // should be ok
macro_rules! qux { ($x:pat_param | $y:pat) => {} } // should be ok
macro_rules! ogg { ($x:pat | $y:pat_param) => {} }
@ -30,4 +35,5 @@ fn main() {
let result: Result<i64, i32> = Err(42);
let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into());
assert_eq!(int, 42);
a!(1|);
}

View File

@ -1,11 +1,11 @@
error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
--> $DIR/macro-or-patterns-back-compat.rs:6:21
--> $DIR/macro-or-patterns-back-compat.rs:10:21
|
LL | macro_rules! foo { ($x:pat | $y:pat) => {} }
| ^^^^^^ help: use pat_param to preserve semantics: `$x:pat_param`
|
note: the lint level is defined here
--> $DIR/macro-or-patterns-back-compat.rs:3:9
--> $DIR/macro-or-patterns-back-compat.rs:4:9
|
LL | #![deny(or_patterns_back_compat)]
| ^^^^^^^^^^^^^^^^^^^^^^^
@ -13,7 +13,7 @@ LL | #![deny(or_patterns_back_compat)]
= note: for more information, see issue #84869 <https://github.com/rust-lang/rust/issues/84869>
error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
--> $DIR/macro-or-patterns-back-compat.rs:9:23
--> $DIR/macro-or-patterns-back-compat.rs:13:23
|
LL | macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} }
| ^^^^^^ help: use pat_param to preserve semantics: `$x:pat_param`
@ -22,7 +22,7 @@ LL | macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} }
= note: for more information, see issue #84869 <https://github.com/rust-lang/rust/issues/84869>
error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
--> $DIR/macro-or-patterns-back-compat.rs:14:21
--> $DIR/macro-or-patterns-back-compat.rs:19:21
|
LL | macro_rules! ogg { ($x:pat | $y:pat_param) => {} }
| ^^^^^^ help: use pat_param to preserve semantics: `$x:pat_param`
@ -31,7 +31,7 @@ LL | macro_rules! ogg { ($x:pat | $y:pat_param) => {} }
= note: for more information, see issue #84869 <https://github.com/rust-lang/rust/issues/84869>
error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
--> $DIR/macro-or-patterns-back-compat.rs:18:26
--> $DIR/macro-or-patterns-back-compat.rs:23:26
|
LL | ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => {
| ^^^^^^^^ help: use pat_param to preserve semantics: `$pat:pat_param`