mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 23:04:33 +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 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 => {}
|
||||||
|
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
|
// 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|);
|
||||||
}
|
}
|
||||||
|
@ -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|);
|
||||||
}
|
}
|
||||||
|
@ -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`
|
||||||
|
Loading…
Reference in New Issue
Block a user