add OR_PATTERNS_BACK_COMPAT lint
test: add more cases test: add comments refine msg
This commit is contained in:
parent
803ddb8359
commit
2c66e15468
7 changed files with 162 additions and 3 deletions
|
@ -18,7 +18,8 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_feature::Features;
|
||||
use rustc_lint_defs::builtin::SEMICOLON_IN_EXPRESSIONS_FROM_MACROS;
|
||||
use rustc_lint_defs::builtin::{OR_PATTERNS_BACK_COMPAT, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS};
|
||||
use rustc_lint_defs::BuiltinLintDiagnostics;
|
||||
use rustc_parse::parser::Parser;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_session::Session;
|
||||
|
@ -951,8 +952,32 @@ fn check_matcher_core(
|
|||
// Now `last` holds the complete set of NT tokens that could
|
||||
// end the sequence before SUFFIX. Check that every one works with `suffix`.
|
||||
for token in &last.tokens {
|
||||
if let TokenTree::MetaVarDecl(_, name, Some(kind)) = *token {
|
||||
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::Pat2015 { 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 pat2015, for example: $x:pat -> $x:pat2015.
|
||||
let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
|
||||
span,
|
||||
name,
|
||||
Some(NonterminalKind::Pat2015 { inferred: false }),
|
||||
));
|
||||
sess.buffer_lint_with_diagnostic(
|
||||
&OR_PATTERNS_BACK_COMPAT,
|
||||
span,
|
||||
ast::CRATE_NODE_ID,
|
||||
&*format!("the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro",),
|
||||
BuiltinLintDiagnostics::OrPatternsBackCompat(
|
||||
span, suggestion,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match is_in_follow(next_token, kind) {
|
||||
IsInFollow::Yes => {}
|
||||
IsInFollow::No(possible) => {
|
||||
|
@ -1080,7 +1105,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
|
|||
_ => IsInFollow::No(TOKENS),
|
||||
}
|
||||
}
|
||||
NonterminalKind::Pat2015 { .. } | NonterminalKind::Pat2021 { .. } => {
|
||||
NonterminalKind::Pat2015 { .. } => {
|
||||
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
|
||||
match tok {
|
||||
TokenTree::Token(token) => match token.kind {
|
||||
|
@ -1091,6 +1116,17 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
|
|||
_ => IsInFollow::No(TOKENS),
|
||||
}
|
||||
}
|
||||
NonterminalKind::Pat2021 { .. } => {
|
||||
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"];
|
||||
match tok {
|
||||
TokenTree::Token(token) => match token.kind {
|
||||
FatArrow | Comma | Eq => IsInFollow::Yes,
|
||||
Ident(name, false) if name == kw::If || name == kw::In => IsInFollow::Yes,
|
||||
_ => IsInFollow::No(TOKENS),
|
||||
},
|
||||
_ => IsInFollow::No(TOKENS),
|
||||
}
|
||||
}
|
||||
NonterminalKind::Path | NonterminalKind::Ty => {
|
||||
const TOKENS: &[&str] = &[
|
||||
"`{`", "`[`", "`=>`", "`,`", "`>`", "`=`", "`:`", "`;`", "`|`", "`as`",
|
||||
|
|
|
@ -709,6 +709,9 @@ pub trait LintContext: Sized {
|
|||
BuiltinLintDiagnostics::ProcMacroBackCompat(note) => {
|
||||
db.note(¬e);
|
||||
}
|
||||
BuiltinLintDiagnostics::OrPatternsBackCompat(span,suggestion) => {
|
||||
db.span_suggestion(span, "use pat2015 to preserve semantics", suggestion, Applicability::MachineApplicable);
|
||||
}
|
||||
}
|
||||
// Rewrap `db`, and pass control to the user.
|
||||
decorate(LintDiagnosticBuilder::new(db));
|
||||
|
|
|
@ -2959,6 +2959,7 @@ declare_lint_pass! {
|
|||
DISJOINT_CAPTURE_DROP_REORDER,
|
||||
LEGACY_DERIVE_HELPERS,
|
||||
PROC_MACRO_BACK_COMPAT,
|
||||
OR_PATTERNS_BACK_COMPAT,
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -3136,3 +3137,37 @@ declare_lint! {
|
|||
})
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `or_patterns_back_compat` lint detects usage of old versions of or-patterns.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #![deny(or_patterns_back_compat)]
|
||||
/// macro_rules! match_any {
|
||||
/// ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => {
|
||||
/// match $expr {
|
||||
/// $(
|
||||
/// $( $pat => $expr_arm, )+
|
||||
/// )+
|
||||
/// }
|
||||
/// };
|
||||
/// }
|
||||
///
|
||||
/// 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);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// In Rust 2021, the pat matcher will match new patterns, which include the | character.
|
||||
pub OR_PATTERNS_BACK_COMPAT,
|
||||
Allow,
|
||||
"detects usage of old versions of or-patterns",
|
||||
}
|
||||
|
|
|
@ -267,6 +267,7 @@ pub enum BuiltinLintDiagnostics {
|
|||
LegacyDeriveHelpers(Span),
|
||||
ExternDepSpec(String, ExternDepSpec),
|
||||
ProcMacroBackCompat(String),
|
||||
OrPatternsBackCompat(Span, String),
|
||||
}
|
||||
|
||||
/// Lints that are buffered up early on in the `Session` before the
|
||||
|
|
26
src/test/ui/macros/macro-or-patterns-back-compat.fixed
Normal file
26
src/test/ui/macros/macro-or-patterns-back-compat.fixed
Normal file
|
@ -0,0 +1,26 @@
|
|||
// ignore-tidy-linelength
|
||||
// run-rustfix
|
||||
|
||||
#![feature(edition_macro_pats)]
|
||||
#![deny(or_patterns_back_compat)]
|
||||
#![allow(unused_macros)]
|
||||
macro_rules! foo { ($x:pat2015 | $y:pat) => {} } //~ ERROR the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro
|
||||
macro_rules! bar { ($($x:pat2015)+ | $($y:pat)+) => {} } //~ ERROR the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro
|
||||
macro_rules! baz { ($x:pat2015 | $y:pat2015) => {} } // should be ok
|
||||
macro_rules! qux { ($x:pat2015 | $y:pat) => {} } // should be ok
|
||||
macro_rules! ogg { ($x:pat2015 | $y:pat2015) => {} } //~ ERROR the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro
|
||||
macro_rules! match_any {
|
||||
( $expr:expr , $( $( $pat:pat2015 )|+ => $expr_arm:expr ),+ ) => { //~ ERROR the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro
|
||||
match $expr {
|
||||
$(
|
||||
$( $pat => $expr_arm, )+
|
||||
)+
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
26
src/test/ui/macros/macro-or-patterns-back-compat.rs
Normal file
26
src/test/ui/macros/macro-or-patterns-back-compat.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
// ignore-tidy-linelength
|
||||
// run-rustfix
|
||||
|
||||
#![feature(edition_macro_pats)]
|
||||
#![deny(or_patterns_back_compat)]
|
||||
#![allow(unused_macros)]
|
||||
macro_rules! foo { ($x:pat | $y:pat) => {} } //~ ERROR the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro
|
||||
macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } //~ ERROR the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro
|
||||
macro_rules! baz { ($x:pat2015 | $y:pat2015) => {} } // should be ok
|
||||
macro_rules! qux { ($x:pat2015 | $y:pat) => {} } // should be ok
|
||||
macro_rules! ogg { ($x:pat | $y:pat2015) => {} } //~ ERROR the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro
|
||||
macro_rules! match_any {
|
||||
( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { //~ ERROR the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro
|
||||
match $expr {
|
||||
$(
|
||||
$( $pat => $expr_arm, )+
|
||||
)+
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
32
src/test/ui/macros/macro-or-patterns-back-compat.stderr
Normal file
32
src/test/ui/macros/macro-or-patterns-back-compat.stderr
Normal file
|
@ -0,0 +1,32 @@
|
|||
error: the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro
|
||||
--> $DIR/macro-or-patterns-back-compat.rs:7:21
|
||||
|
|
||||
LL | macro_rules! foo { ($x:pat | $y:pat) => {} }
|
||||
| ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/macro-or-patterns-back-compat.rs:5:9
|
||||
|
|
||||
LL | #![deny(or_patterns_back_compat)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro
|
||||
--> $DIR/macro-or-patterns-back-compat.rs:8:23
|
||||
|
|
||||
LL | macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} }
|
||||
| ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015`
|
||||
|
||||
error: the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro
|
||||
--> $DIR/macro-or-patterns-back-compat.rs:11:21
|
||||
|
|
||||
LL | macro_rules! ogg { ($x:pat | $y:pat2015) => {} }
|
||||
| ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015`
|
||||
|
||||
error: the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro
|
||||
--> $DIR/macro-or-patterns-back-compat.rs:13:26
|
||||
|
|
||||
LL | ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => {
|
||||
| ^^^^^^^^ help: use pat2015 to preserve semantics: `$pat:pat2015`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
Loading…
Add table
Reference in a new issue