mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 08:44:35 +00:00
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:
commit
079aa837d2
@ -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 => {}
|
||||
|
6
src/test/ui/macros/auxiliary/or-pattern.rs
Normal file
6
src/test/ui/macros/auxiliary/or-pattern.rs
Normal file
@ -0,0 +1,6 @@
|
||||
#![crate_type = "lib"]
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! a {
|
||||
($x:pat|) => ();
|
||||
}
|
@ -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|);
|
||||
}
|
||||
|
@ -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|);
|
||||
}
|
||||
|
@ -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`
|
||||
|
Loading…
Reference in New Issue
Block a user