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

View File

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