From 06db2104597b405a643c02307be68d1188a95f61 Mon Sep 17 00:00:00 2001
From: Mara Bos <m-ou.se@m-ou.se>
Date: Wed, 23 Jun 2021 14:07:32 +0000
Subject: [PATCH] Don't lint :pat when re-parsing a macro from another crate.

---
 compiler/rustc_expand/src/mbe/macro_rules.rs  | 66 ++++++++++---------
 src/test/ui/macros/auxiliary/or-pattern.rs    |  6 ++
 .../macro-or-patterns-back-compat.fixed       |  6 ++
 .../macros/macro-or-patterns-back-compat.rs   |  6 ++
 .../macro-or-patterns-back-compat.stderr      | 10 +--
 5 files changed, 57 insertions(+), 37 deletions(-)
 create mode 100644 src/test/ui/macros/auxiliary/or-pattern.rs

diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index abad190b072..a255b4f83ac 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -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 => {}
diff --git a/src/test/ui/macros/auxiliary/or-pattern.rs b/src/test/ui/macros/auxiliary/or-pattern.rs
new file mode 100644
index 00000000000..a319c405eb6
--- /dev/null
+++ b/src/test/ui/macros/auxiliary/or-pattern.rs
@@ -0,0 +1,6 @@
+#![crate_type = "lib"]
+
+#[macro_export]
+macro_rules! a {
+    ($x:pat|) => ();
+}
diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.fixed b/src/test/ui/macros/macro-or-patterns-back-compat.fixed
index f4e81a6be2a..70425429278 100644
--- a/src/test/ui/macros/macro-or-patterns-back-compat.fixed
+++ b/src/test/ui/macros/macro-or-patterns-back-compat.fixed
@@ -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|);
 }
diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.rs b/src/test/ui/macros/macro-or-patterns-back-compat.rs
index 49affdd38da..b19942a830e 100644
--- a/src/test/ui/macros/macro-or-patterns-back-compat.rs
+++ b/src/test/ui/macros/macro-or-patterns-back-compat.rs
@@ -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|);
 }
diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.stderr b/src/test/ui/macros/macro-or-patterns-back-compat.stderr
index 62687eb36b8..4f5a450518f 100644
--- a/src/test/ui/macros/macro-or-patterns-back-compat.stderr
+++ b/src/test/ui/macros/macro-or-patterns-back-compat.stderr
@@ -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`