Skip to content

Commit 36bcf40

Browse files
committed
Auto merge of rust-lang#83468 - hi-rustin:rustin-patch-lint, r=nikomatsakis
add OR_PATTERNS_BACK_COMPAT lint close rust-lang#83318
2 parents a207871 + aa987c2 commit 36bcf40

File tree

7 files changed

+162
-3
lines changed

7 files changed

+162
-3
lines changed

compiler/rustc_expand/src/mbe/macro_rules.rs

+39-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ use rustc_data_structures::fx::FxHashMap;
1818
use rustc_data_structures::sync::Lrc;
1919
use rustc_errors::{Applicability, DiagnosticBuilder};
2020
use rustc_feature::Features;
21-
use rustc_lint_defs::builtin::SEMICOLON_IN_EXPRESSIONS_FROM_MACROS;
21+
use rustc_lint_defs::builtin::{OR_PATTERNS_BACK_COMPAT, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS};
22+
use rustc_lint_defs::BuiltinLintDiagnostics;
2223
use rustc_parse::parser::Parser;
2324
use rustc_session::parse::ParseSess;
2425
use rustc_session::Session;
@@ -951,8 +952,32 @@ fn check_matcher_core(
951952
// Now `last` holds the complete set of NT tokens that could
952953
// end the sequence before SUFFIX. Check that every one works with `suffix`.
953954
for token in &last.tokens {
954-
if let TokenTree::MetaVarDecl(_, name, Some(kind)) = *token {
955+
if let TokenTree::MetaVarDecl(span, name, Some(kind)) = *token {
955956
for next_token in &suffix_first.tokens {
957+
// Check if the old pat is used and the next token is `|`.
958+
if let NonterminalKind::Pat2015 { inferred: true } = kind {
959+
if let TokenTree::Token(token) = next_token {
960+
if let BinOp(token) = token.kind {
961+
if let token::BinOpToken::Or = token {
962+
// It is suggestion to use pat2015, for example: $x:pat -> $x:pat2015.
963+
let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
964+
span,
965+
name,
966+
Some(NonterminalKind::Pat2015 { inferred: false }),
967+
));
968+
sess.buffer_lint_with_diagnostic(
969+
&OR_PATTERNS_BACK_COMPAT,
970+
span,
971+
ast::CRATE_NODE_ID,
972+
&*format!("the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro",),
973+
BuiltinLintDiagnostics::OrPatternsBackCompat(
974+
span, suggestion,
975+
),
976+
);
977+
}
978+
}
979+
}
980+
}
956981
match is_in_follow(next_token, kind) {
957982
IsInFollow::Yes => {}
958983
IsInFollow::No(possible) => {
@@ -1080,7 +1105,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
10801105
_ => IsInFollow::No(TOKENS),
10811106
}
10821107
}
1083-
NonterminalKind::Pat2015 { .. } | NonterminalKind::Pat2021 { .. } => {
1108+
NonterminalKind::Pat2015 { .. } => {
10841109
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
10851110
match tok {
10861111
TokenTree::Token(token) => match token.kind {
@@ -1091,6 +1116,17 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
10911116
_ => IsInFollow::No(TOKENS),
10921117
}
10931118
}
1119+
NonterminalKind::Pat2021 { .. } => {
1120+
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"];
1121+
match tok {
1122+
TokenTree::Token(token) => match token.kind {
1123+
FatArrow | Comma | Eq => IsInFollow::Yes,
1124+
Ident(name, false) if name == kw::If || name == kw::In => IsInFollow::Yes,
1125+
_ => IsInFollow::No(TOKENS),
1126+
},
1127+
_ => IsInFollow::No(TOKENS),
1128+
}
1129+
}
10941130
NonterminalKind::Path | NonterminalKind::Ty => {
10951131
const TOKENS: &[&str] = &[
10961132
"`{`", "`[`", "`=>`", "`,`", "`>`", "`=`", "`:`", "`;`", "`|`", "`as`",

compiler/rustc_lint/src/context.rs

+3
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,9 @@ pub trait LintContext: Sized {
709709
BuiltinLintDiagnostics::ProcMacroBackCompat(note) => {
710710
db.note(&note);
711711
}
712+
BuiltinLintDiagnostics::OrPatternsBackCompat(span,suggestion) => {
713+
db.span_suggestion(span, "use pat2015 to preserve semantics", suggestion, Applicability::MachineApplicable);
714+
}
712715
}
713716
// Rewrap `db`, and pass control to the user.
714717
decorate(LintDiagnosticBuilder::new(db));

compiler/rustc_lint_defs/src/builtin.rs

+35
Original file line numberDiff line numberDiff line change
@@ -2959,6 +2959,7 @@ declare_lint_pass! {
29592959
DISJOINT_CAPTURE_DROP_REORDER,
29602960
LEGACY_DERIVE_HELPERS,
29612961
PROC_MACRO_BACK_COMPAT,
2962+
OR_PATTERNS_BACK_COMPAT,
29622963
]
29632964
}
29642965

@@ -3136,3 +3137,37 @@ declare_lint! {
31363137
})
31373138
};
31383139
}
3140+
3141+
declare_lint! {
3142+
/// The `or_patterns_back_compat` lint detects usage of old versions of or-patterns.
3143+
///
3144+
/// ### Example
3145+
///
3146+
/// ```rust,compile_fail
3147+
/// #![deny(or_patterns_back_compat)]
3148+
/// macro_rules! match_any {
3149+
/// ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => {
3150+
/// match $expr {
3151+
/// $(
3152+
/// $( $pat => $expr_arm, )+
3153+
/// )+
3154+
/// }
3155+
/// };
3156+
/// }
3157+
///
3158+
/// fn main() {
3159+
/// let result: Result<i64, i32> = Err(42);
3160+
/// let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into());
3161+
/// assert_eq!(int, 42);
3162+
/// }
3163+
/// ```
3164+
///
3165+
/// {{produces}}
3166+
///
3167+
/// ### Explanation
3168+
///
3169+
/// In Rust 2021, the pat matcher will match new patterns, which include the | character.
3170+
pub OR_PATTERNS_BACK_COMPAT,
3171+
Allow,
3172+
"detects usage of old versions of or-patterns",
3173+
}

compiler/rustc_lint_defs/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ pub enum BuiltinLintDiagnostics {
267267
LegacyDeriveHelpers(Span),
268268
ExternDepSpec(String, ExternDepSpec),
269269
ProcMacroBackCompat(String),
270+
OrPatternsBackCompat(Span, String),
270271
}
271272

272273
/// Lints that are buffered up early on in the `Session` before the
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// ignore-tidy-linelength
2+
// run-rustfix
3+
4+
#![feature(edition_macro_pats)]
5+
#![deny(or_patterns_back_compat)]
6+
#![allow(unused_macros)]
7+
macro_rules! foo { ($x:pat2015 | $y:pat) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
8+
macro_rules! bar { ($($x:pat2015)+ | $($y:pat)+) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
9+
macro_rules! baz { ($x:pat2015 | $y:pat2015) => {} } // should be ok
10+
macro_rules! qux { ($x:pat2015 | $y:pat) => {} } // should be ok
11+
macro_rules! ogg { ($x:pat2015 | $y:pat2015) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
12+
macro_rules! match_any {
13+
( $expr:expr , $( $( $pat:pat2015 )|+ => $expr_arm:expr ),+ ) => { //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
14+
match $expr {
15+
$(
16+
$( $pat => $expr_arm, )+
17+
)+
18+
}
19+
};
20+
}
21+
22+
fn main() {
23+
let result: Result<i64, i32> = Err(42);
24+
let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into());
25+
assert_eq!(int, 42);
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// ignore-tidy-linelength
2+
// run-rustfix
3+
4+
#![feature(edition_macro_pats)]
5+
#![deny(or_patterns_back_compat)]
6+
#![allow(unused_macros)]
7+
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
8+
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
9+
macro_rules! baz { ($x:pat2015 | $y:pat2015) => {} } // should be ok
10+
macro_rules! qux { ($x:pat2015 | $y:pat) => {} } // should be ok
11+
macro_rules! ogg { ($x:pat | $y:pat2015) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
12+
macro_rules! match_any {
13+
( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
14+
match $expr {
15+
$(
16+
$( $pat => $expr_arm, )+
17+
)+
18+
}
19+
};
20+
}
21+
22+
fn main() {
23+
let result: Result<i64, i32> = Err(42);
24+
let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into());
25+
assert_eq!(int, 42);
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
2+
--> $DIR/macro-or-patterns-back-compat.rs:7:21
3+
|
4+
LL | macro_rules! foo { ($x:pat | $y:pat) => {} }
5+
| ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015`
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/macro-or-patterns-back-compat.rs:5:9
9+
|
10+
LL | #![deny(or_patterns_back_compat)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
14+
--> $DIR/macro-or-patterns-back-compat.rs:8:23
15+
|
16+
LL | macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} }
17+
| ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015`
18+
19+
error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
20+
--> $DIR/macro-or-patterns-back-compat.rs:11:21
21+
|
22+
LL | macro_rules! ogg { ($x:pat | $y:pat2015) => {} }
23+
| ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015`
24+
25+
error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
26+
--> $DIR/macro-or-patterns-back-compat.rs:13:26
27+
|
28+
LL | ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => {
29+
| ^^^^^^^^ help: use pat2015 to preserve semantics: `$pat:pat2015`
30+
31+
error: aborting due to 4 previous errors
32+

0 commit comments

Comments
 (0)